All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v5 00/17] qapi doc generation (whole version, squashed)
@ 2016-11-17 15:54 Marc-André Lureau
  2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 01/17] qapi: improve device_add schema Marc-André Lureau
                   ` (17 more replies)
  0 siblings, 18 replies; 48+ messages in thread
From: Marc-André Lureau @ 2016-11-17 15:54 UTC (permalink / raw)
  To: qemu-devel; +Cc: eblake, armbru, Marc-André Lureau

Add a qapi2texi script to generate the documentation from the qapi
schemas.

The 15th patch in this series is a squashed version of the
documentation move from qmp-commands.txt to the schemas. The whole
version (not sent on the ML to avoid spamming) is in the following git
branch: https://github.com/elmarco/qemu/commits/qapi-doc

PDF preview:
https://fedorapeople.org/~elmarco/qemu-qmp-ref.pdf

v5:
- many parser and generator fixes and improvements after v4 review:
 - simplified current section handling by using a Section object
 - adding a line is more stateful: either freeform or symbol comment
 - always check_docs() when parsing with QAPISchema
 - simplified some code and comments
 - do not break current section on empty line, but break after a non
   indented paragraph in an argument section. This seems to reflects
   the way documentation is written:

   ##
   # @foo:
   # @arg: fluctuat nec mergitur
   #       - continues here
   #
   #       Since: 1853
   #
   # Body
   #
   ##

   Other sections (Note/Examples etc) are not indented (it seems), but
   could use a similar rule. I prefer to keep this only for args, for
   styling reasons (bikeshedding?).

- better handling of flat-union in generator
- list all enum values (even when not documented)
- added qapi-doc parsing tests and more error checking
- pep8/pylint fixes
- some more schema doc fixes
- do not move logo to docs/

v4:
- more device_add schema fixes
- do not merge docs/qmp-intro.txt in qemu-qmp-ref.texi
- remove needless @ifinfo, add GPL copying text
- added qemu logo to pdf
- added some r-b tags

v3:
- many improvements to the doc parser:
  - throws an error in various malformated conditions
  - allows multiple meta-sections, except for "Since:" and "Return:"
  - build a list of docs, instead of attaching docs to expressions
  - accept() breaks on new doc block, and get_doc() returns a QAPIDoc
- fix more documentation to fit the new parser
- use a master texi file that includes the generated file, instead of
  templated texi file
- texi fixes after Markus review
- only build and install html and man pages by default
- fix .gitignore

v2:
- change licence to be lgpl2+
- fix some comments & commit message
- add more code comments
- improve the doc parsing to treat only "Since" as a special case not
  requiring ":" (common notation in the doc)
- include some early schema doc fixes (to fix generated doc)
- include the squashed version of the doc move
- include the man page and installation build changes

Marc-André Lureau (17):
  qapi: improve device_add schema
  qga/schema: fix double-return in doc
  qga/schema: improve guest-set-vcpus Returns: section
  qapi: fix schema symbol sections
  qapi: fix missing symbol @prefix
  qapi: fix various symbols mismatch in documentation
  qapi: use one symbol per line
  qapi: add missing colon-ending for section name
  qapi: add some sections in docs
  qapi: improve TransactionAction doc
  docs: add master qapi texi files
  qapi: rename QAPIExprError/QAPILineError
  qapi: add qapi2texi script
  texi2pod: learn quotation, deftp and deftypefn
  (SQUASHED) move doc to schema
  docs: add qemu logo
  build-sys: add qapi doc generation targets

 Makefile                                     |   57 +-
 tests/Makefile.include                       |   17 +
 scripts/qapi.py                              |  361 ++-
 scripts/qapi2texi.py                         |  331 +++
 scripts/texi2pod.pl                          |   53 +-
 .gitignore                                   |   11 +-
 docs/qapi-code-gen.txt                       |   54 +-
 docs/qemu-ga-ref.texi                        |   82 +
 docs/qemu-qmp-ref.texi                       |   82 +
 docs/qemu_logo.pdf                           |  Bin 0 -> 9117 bytes
 docs/qmp-commands.txt                        | 3824 --------------------------
 docs/qmp-events.txt                          |  731 -----
 docs/qmp-intro.txt                           |    3 +-
 qapi-schema.json                             | 1662 +++++++++--
 qapi/block-core.json                         |  894 +++++-
 qapi/block.json                              |   82 +-
 qapi/common.json                             |   52 +-
 qapi/crypto.json                             |   41 +-
 qapi/event.json                              |  304 +-
 qapi/introspect.json                         |   28 +-
 qapi/rocker.json                             |   63 +-
 qapi/trace.json                              |   25 +-
 qga/qapi-schema.json                         |   64 +-
 tests/qapi-schema/doc-bad-args.err           |    1 +
 tests/qapi-schema/doc-bad-args.exit          |    1 +
 tests/qapi-schema/doc-bad-args.json          |    6 +
 tests/qapi-schema/doc-bad-args.out           |    0
 tests/qapi-schema/doc-bad-symbol.err         |    1 +
 tests/qapi-schema/doc-bad-symbol.exit        |    1 +
 tests/qapi-schema/doc-bad-symbol.json        |    4 +
 tests/qapi-schema/doc-bad-symbol.out         |    0
 tests/qapi-schema/doc-duplicated-arg.err     |    1 +
 tests/qapi-schema/doc-duplicated-arg.exit    |    1 +
 tests/qapi-schema/doc-duplicated-arg.json    |    5 +
 tests/qapi-schema/doc-duplicated-arg.out     |    0
 tests/qapi-schema/doc-duplicated-return.err  |    1 +
 tests/qapi-schema/doc-duplicated-return.exit |    1 +
 tests/qapi-schema/doc-duplicated-return.json |    6 +
 tests/qapi-schema/doc-duplicated-return.out  |    0
 tests/qapi-schema/doc-duplicated-since.err   |    1 +
 tests/qapi-schema/doc-duplicated-since.exit  |    1 +
 tests/qapi-schema/doc-duplicated-since.json  |    6 +
 tests/qapi-schema/doc-duplicated-since.out   |    0
 tests/qapi-schema/doc-empty-arg.err          |    1 +
 tests/qapi-schema/doc-empty-arg.exit         |    1 +
 tests/qapi-schema/doc-empty-arg.json         |    4 +
 tests/qapi-schema/doc-empty-arg.out          |    0
 tests/qapi-schema/doc-empty-section.err      |    1 +
 tests/qapi-schema/doc-empty-section.exit     |    1 +
 tests/qapi-schema/doc-empty-section.json     |    6 +
 tests/qapi-schema/doc-empty-section.out      |    0
 tests/qapi-schema/doc-empty-symbol.err       |    1 +
 tests/qapi-schema/doc-empty-symbol.exit      |    1 +
 tests/qapi-schema/doc-empty-symbol.json      |    3 +
 tests/qapi-schema/doc-empty-symbol.out       |    0
 tests/qapi-schema/doc-invalid-end.err        |    1 +
 tests/qapi-schema/doc-invalid-end.exit       |    1 +
 tests/qapi-schema/doc-invalid-end.json       |    3 +
 tests/qapi-schema/doc-invalid-end.out        |    0
 tests/qapi-schema/doc-invalid-end2.err       |    1 +
 tests/qapi-schema/doc-invalid-end2.exit      |    1 +
 tests/qapi-schema/doc-invalid-end2.json      |    3 +
 tests/qapi-schema/doc-invalid-end2.out       |    0
 tests/qapi-schema/doc-invalid-return.err     |    1 +
 tests/qapi-schema/doc-invalid-return.exit    |    1 +
 tests/qapi-schema/doc-invalid-return.json    |    5 +
 tests/qapi-schema/doc-invalid-return.out     |    0
 tests/qapi-schema/doc-invalid-section.err    |    1 +
 tests/qapi-schema/doc-invalid-section.exit   |    1 +
 tests/qapi-schema/doc-invalid-section.json   |    4 +
 tests/qapi-schema/doc-invalid-section.out    |    0
 tests/qapi-schema/doc-invalid-start.err      |    1 +
 tests/qapi-schema/doc-invalid-start.exit     |    1 +
 tests/qapi-schema/doc-invalid-start.json     |    3 +
 tests/qapi-schema/doc-invalid-start.out      |    0
 tests/qapi-schema/doc-missing-expr.err       |    1 +
 tests/qapi-schema/doc-missing-expr.exit      |    1 +
 tests/qapi-schema/doc-missing-expr.json      |    3 +
 tests/qapi-schema/doc-missing-expr.out       |    0
 tests/qapi-schema/doc-missing-space.err      |    1 +
 tests/qapi-schema/doc-missing-space.exit     |    1 +
 tests/qapi-schema/doc-missing-space.json     |    4 +
 tests/qapi-schema/doc-missing-space.out      |    0
 tests/qapi-schema/qapi-schema-test.json      |   58 +
 tests/qapi-schema/qapi-schema-test.out       |   49 +
 tests/qapi-schema/test-qapi.py               |   12 +
 86 files changed, 3922 insertions(+), 5113 deletions(-)
 create mode 100755 scripts/qapi2texi.py
 create mode 100644 docs/qemu-ga-ref.texi
 create mode 100644 docs/qemu-qmp-ref.texi
 create mode 100644 docs/qemu_logo.pdf
 delete mode 100644 docs/qmp-commands.txt
 delete mode 100644 docs/qmp-events.txt
 create mode 100644 tests/qapi-schema/doc-bad-args.err
 create mode 100644 tests/qapi-schema/doc-bad-args.exit
 create mode 100644 tests/qapi-schema/doc-bad-args.json
 create mode 100644 tests/qapi-schema/doc-bad-args.out
 create mode 100644 tests/qapi-schema/doc-bad-symbol.err
 create mode 100644 tests/qapi-schema/doc-bad-symbol.exit
 create mode 100644 tests/qapi-schema/doc-bad-symbol.json
 create mode 100644 tests/qapi-schema/doc-bad-symbol.out
 create mode 100644 tests/qapi-schema/doc-duplicated-arg.err
 create mode 100644 tests/qapi-schema/doc-duplicated-arg.exit
 create mode 100644 tests/qapi-schema/doc-duplicated-arg.json
 create mode 100644 tests/qapi-schema/doc-duplicated-arg.out
 create mode 100644 tests/qapi-schema/doc-duplicated-return.err
 create mode 100644 tests/qapi-schema/doc-duplicated-return.exit
 create mode 100644 tests/qapi-schema/doc-duplicated-return.json
 create mode 100644 tests/qapi-schema/doc-duplicated-return.out
 create mode 100644 tests/qapi-schema/doc-duplicated-since.err
 create mode 100644 tests/qapi-schema/doc-duplicated-since.exit
 create mode 100644 tests/qapi-schema/doc-duplicated-since.json
 create mode 100644 tests/qapi-schema/doc-duplicated-since.out
 create mode 100644 tests/qapi-schema/doc-empty-arg.err
 create mode 100644 tests/qapi-schema/doc-empty-arg.exit
 create mode 100644 tests/qapi-schema/doc-empty-arg.json
 create mode 100644 tests/qapi-schema/doc-empty-arg.out
 create mode 100644 tests/qapi-schema/doc-empty-section.err
 create mode 100644 tests/qapi-schema/doc-empty-section.exit
 create mode 100644 tests/qapi-schema/doc-empty-section.json
 create mode 100644 tests/qapi-schema/doc-empty-section.out
 create mode 100644 tests/qapi-schema/doc-empty-symbol.err
 create mode 100644 tests/qapi-schema/doc-empty-symbol.exit
 create mode 100644 tests/qapi-schema/doc-empty-symbol.json
 create mode 100644 tests/qapi-schema/doc-empty-symbol.out
 create mode 100644 tests/qapi-schema/doc-invalid-end.err
 create mode 100644 tests/qapi-schema/doc-invalid-end.exit
 create mode 100644 tests/qapi-schema/doc-invalid-end.json
 create mode 100644 tests/qapi-schema/doc-invalid-end.out
 create mode 100644 tests/qapi-schema/doc-invalid-end2.err
 create mode 100644 tests/qapi-schema/doc-invalid-end2.exit
 create mode 100644 tests/qapi-schema/doc-invalid-end2.json
 create mode 100644 tests/qapi-schema/doc-invalid-end2.out
 create mode 100644 tests/qapi-schema/doc-invalid-return.err
 create mode 100644 tests/qapi-schema/doc-invalid-return.exit
 create mode 100644 tests/qapi-schema/doc-invalid-return.json
 create mode 100644 tests/qapi-schema/doc-invalid-return.out
 create mode 100644 tests/qapi-schema/doc-invalid-section.err
 create mode 100644 tests/qapi-schema/doc-invalid-section.exit
 create mode 100644 tests/qapi-schema/doc-invalid-section.json
 create mode 100644 tests/qapi-schema/doc-invalid-section.out
 create mode 100644 tests/qapi-schema/doc-invalid-start.err
 create mode 100644 tests/qapi-schema/doc-invalid-start.exit
 create mode 100644 tests/qapi-schema/doc-invalid-start.json
 create mode 100644 tests/qapi-schema/doc-invalid-start.out
 create mode 100644 tests/qapi-schema/doc-missing-expr.err
 create mode 100644 tests/qapi-schema/doc-missing-expr.exit
 create mode 100644 tests/qapi-schema/doc-missing-expr.json
 create mode 100644 tests/qapi-schema/doc-missing-expr.out
 create mode 100644 tests/qapi-schema/doc-missing-space.err
 create mode 100644 tests/qapi-schema/doc-missing-space.exit
 create mode 100644 tests/qapi-schema/doc-missing-space.json
 create mode 100644 tests/qapi-schema/doc-missing-space.out

-- 
2.10.0

^ permalink raw reply	[flat|nested] 48+ messages in thread

* [Qemu-devel] [PATCH v5 01/17] qapi: improve device_add schema
  2016-11-17 15:54 [Qemu-devel] [PATCH v5 00/17] qapi doc generation (whole version, squashed) Marc-André Lureau
@ 2016-11-17 15:54 ` Marc-André Lureau
  2016-11-17 17:38   ` Markus Armbruster
  2016-11-17 19:49   ` Eric Blake
  2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 02/17] qga/schema: fix double-return in doc Marc-André Lureau
                   ` (16 subsequent siblings)
  17 siblings, 2 replies; 48+ messages in thread
From: Marc-André Lureau @ 2016-11-17 15:54 UTC (permalink / raw)
  To: qemu-devel; +Cc: eblake, armbru, Marc-André Lureau

'device_add' is still incomplete for now, but we can fix a few
arguments:
- 'bus' is a common argument, regardless of the device
- 'id' is an optionnal argument

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 qapi-schema.json | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/qapi-schema.json b/qapi-schema.json
index b0b4bf6..9201330 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -2287,7 +2287,7 @@
 #
 # @bus: #optional the device's parent bus (device tree path)
 #
-# @id: the device's ID, must be unique
+# @id: #optional the device's ID, must be unique
 #
 # Additional arguments depend on the type.
 #
@@ -2317,7 +2317,7 @@
 # Since: 0.13
 ##
 { 'command': 'device_add',
-  'data': {'driver': 'str', 'id': 'str'},
+  'data': {'driver': 'str', '*bus': 'str', '*id': 'str'},
   'gen': false } # so we can get the additional arguments
 
 ##
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [Qemu-devel] [PATCH v5 02/17] qga/schema: fix double-return in doc
  2016-11-17 15:54 [Qemu-devel] [PATCH v5 00/17] qapi doc generation (whole version, squashed) Marc-André Lureau
  2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 01/17] qapi: improve device_add schema Marc-André Lureau
@ 2016-11-17 15:54 ` Marc-André Lureau
  2016-11-17 17:38   ` Markus Armbruster
  2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 03/17] qga/schema: improve guest-set-vcpus Returns: section Marc-André Lureau
                   ` (15 subsequent siblings)
  17 siblings, 1 reply; 48+ messages in thread
From: Marc-André Lureau @ 2016-11-17 15:54 UTC (permalink / raw)
  To: qemu-devel; +Cc: eblake, armbru, Marc-André Lureau

guest-get-memory-block-info documentation should have only one
"Returns:".

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
---
 qga/qapi-schema.json | 1 -
 1 file changed, 1 deletion(-)

diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index c21f308..758803a 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -952,7 +952,6 @@
 #
 # Get information relating to guest memory blocks.
 #
-# Returns: memory block size in bytes.
 # Returns: @GuestMemoryBlockInfo
 #
 # Since 2.3
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [Qemu-devel] [PATCH v5 03/17] qga/schema: improve guest-set-vcpus Returns: section
  2016-11-17 15:54 [Qemu-devel] [PATCH v5 00/17] qapi doc generation (whole version, squashed) Marc-André Lureau
  2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 01/17] qapi: improve device_add schema Marc-André Lureau
  2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 02/17] qga/schema: fix double-return in doc Marc-André Lureau
@ 2016-11-17 15:54 ` Marc-André Lureau
  2016-11-17 17:39   ` Markus Armbruster
  2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 04/17] qapi: fix schema symbol sections Marc-André Lureau
                   ` (14 subsequent siblings)
  17 siblings, 1 reply; 48+ messages in thread
From: Marc-André Lureau @ 2016-11-17 15:54 UTC (permalink / raw)
  To: qemu-devel; +Cc: eblake, armbru, Marc-André Lureau

The documentation parser we are going to add finishes a section after an
empty line.

Fix the Returns: section of guest-set-vcpus, and itemize the possible
return values.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 qga/qapi-schema.json | 12 ++++--------
 1 file changed, 4 insertions(+), 8 deletions(-)

diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index 758803a..cb68c17 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -696,22 +696,18 @@
 #
 # Returns: The length of the initial sublist that has been successfully
 #          processed. The guest agent maximizes this value. Possible cases:
-#
-#          0:                if the @vcpus list was empty on input. Guest state
+#          - 0:              if the @vcpus list was empty on input. Guest state
 #                            has not been changed. Otherwise,
-#
-#          Error:            processing the first node of @vcpus failed for the
+#          - Error:          processing the first node of @vcpus failed for the
 #                            reason returned. Guest state has not been changed.
 #                            Otherwise,
-#
-#          < length(@vcpus): more than zero initial nodes have been processed,
+#          - < length(@vcpus): more than zero initial nodes have been processed,
 #                            but not the entire @vcpus list. Guest state has
 #                            changed accordingly. To retrieve the error
 #                            (assuming it persists), repeat the call with the
 #                            successfully processed initial sublist removed.
 #                            Otherwise,
-#
-#          length(@vcpus):   call successful.
+#          - length(@vcpus): call successful.
 #
 # Since: 1.5
 ##
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [Qemu-devel] [PATCH v5 04/17] qapi: fix schema symbol sections
  2016-11-17 15:54 [Qemu-devel] [PATCH v5 00/17] qapi doc generation (whole version, squashed) Marc-André Lureau
                   ` (2 preceding siblings ...)
  2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 03/17] qga/schema: improve guest-set-vcpus Returns: section Marc-André Lureau
@ 2016-11-17 15:54 ` Marc-André Lureau
  2016-11-17 17:39   ` Markus Armbruster
  2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 05/17] qapi: fix missing symbol @prefix Marc-André Lureau
                   ` (13 subsequent siblings)
  17 siblings, 1 reply; 48+ messages in thread
From: Marc-André Lureau @ 2016-11-17 15:54 UTC (permalink / raw)
  To: qemu-devel; +Cc: eblake, armbru, Marc-André Lureau

According to docs/qapi-code-gen.txt, there needs to be '##' to start a
and end a symbol section, that's also what the documentation parser
expects.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
---
 qapi-schema.json     | 18 +++++++++++++-----
 qapi/block-core.json |  1 +
 qga/qapi-schema.json |  3 +++
 3 files changed, 17 insertions(+), 5 deletions(-)

diff --git a/qapi-schema.json b/qapi-schema.json
index 9201330..8ef6635 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -65,6 +65,7 @@
 { 'enum': 'LostTickPolicy',
   'data': ['discard', 'delay', 'merge', 'slew' ] }
 
+##
 # @add_client
 #
 # Allow client connections for VNC, Spice and socket based
@@ -442,6 +443,7 @@
            'cache-miss': 'int', 'cache-miss-rate': 'number',
            'overflow': 'int' } }
 
+##
 # @MigrationStatus:
 #
 # An enumeration of migration status.
@@ -627,6 +629,7 @@
 ##
 { 'command': 'query-migrate-capabilities', 'returns':   ['MigrationCapabilityStatus']}
 
+##
 # @MigrationParameter
 #
 # Migration parameters enumeration
@@ -685,7 +688,7 @@
            'tls-creds', 'tls-hostname', 'max-bandwidth',
            'downtime-limit', 'x-checkpoint-delay' ] }
 
-#
+##
 # @migrate-set-parameters
 #
 # Set various migration parameters.  See MigrationParameters for details.
@@ -697,7 +700,7 @@
 { 'command': 'migrate-set-parameters', 'boxed': true,
   'data': 'MigrationParameters' }
 
-#
+##
 # @MigrationParameters
 #
 # Optional members can be omitted on input ('migrate-set-parameters')
@@ -795,6 +798,7 @@
 # command.
 #
 # Since: 2.5
+##
 { 'command': 'migrate-start-postcopy' }
 
 ##
@@ -2252,6 +2256,7 @@
 ##
 { 'command': 'migrate-incoming', 'data': {'uri': 'str' } }
 
+##
 # @xen-save-devices-state:
 #
 # Save the state of all devices to file. The RAM and the block devices
@@ -3482,6 +3487,7 @@
             'modelb': 'CpuModelInfo' },
   'returns': 'CpuModelBaselineInfo' }
 
+##
 # @AddfdInfo:
 #
 # Information about a file descriptor that was added to an fd set.
@@ -4530,14 +4536,16 @@
 ##
 { 'command': 'query-memory-devices', 'returns': ['MemoryDeviceInfo'] }
 
-## @ACPISlotType
+##
+# @ACPISlotType
 #
 # @DIMM: memory slot
 # @CPU: logical CPU slot (since 2.7)
-#
+##
 { 'enum': 'ACPISlotType', 'data': [ 'DIMM', 'CPU' ] }
 
-## @ACPIOSTInfo
+##
+# @ACPIOSTInfo
 #
 # OSPM Status Indication for a device
 # For description of possible values of @source and @status fields
diff --git a/qapi/block-core.json b/qapi/block-core.json
index c29bef7..39cdaba 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -2809,6 +2809,7 @@
             'offset': 'int',
             'speed' : 'int' } }
 
+##
 # @PreallocMode
 #
 # Preallocation mode of QEMU image file
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index cb68c17..5173c4a 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -833,6 +833,7 @@
 { 'command': 'guest-set-user-password',
   'data': { 'username': 'str', 'password': 'str', 'crypted': 'bool' } }
 
+##
 # @GuestMemoryBlock:
 #
 # @phys-index: Arbitrary guest-specific unique identifier of the MEMORY BLOCK.
@@ -932,6 +933,7 @@
   'data':    {'mem-blks': ['GuestMemoryBlock'] },
   'returns': ['GuestMemoryBlockResponse'] }
 
+##
 # @GuestMemoryBlockInfo:
 #
 # @size: the size (in bytes) of the guest memory blocks,
@@ -955,6 +957,7 @@
 { 'command': 'guest-get-memory-block-info',
   'returns': 'GuestMemoryBlockInfo' }
 
+##
 # @GuestExecStatus:
 #
 # @exited: true if process has already terminated.
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [Qemu-devel] [PATCH v5 05/17] qapi: fix missing symbol @prefix
  2016-11-17 15:54 [Qemu-devel] [PATCH v5 00/17] qapi doc generation (whole version, squashed) Marc-André Lureau
                   ` (3 preceding siblings ...)
  2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 04/17] qapi: fix schema symbol sections Marc-André Lureau
@ 2016-11-17 15:54 ` Marc-André Lureau
  2016-11-17 17:40   ` Markus Armbruster
  2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 06/17] qapi: fix various symbols mismatch in documentation Marc-André Lureau
                   ` (12 subsequent siblings)
  17 siblings, 1 reply; 48+ messages in thread
From: Marc-André Lureau @ 2016-11-17 15:54 UTC (permalink / raw)
  To: qemu-devel; +Cc: eblake, armbru, Marc-André Lureau

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
---
 qapi-schema.json     |  4 ++--
 qapi/block-core.json |  4 ++--
 qapi/crypto.json     | 36 ++++++++++++++++++------------------
 3 files changed, 22 insertions(+), 22 deletions(-)

diff --git a/qapi-schema.json b/qapi-schema.json
index 8ef6635..09dbc88 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -4650,7 +4650,7 @@
 { 'include': 'qapi/rocker.json' }
 
 ##
-# ReplayMode:
+# @ReplayMode:
 #
 # Mode of the replay subsystem.
 #
@@ -4718,7 +4718,7 @@
 { 'command': 'query-gic-capabilities', 'returns': ['GICCapability'] }
 
 ##
-# CpuInstanceProperties
+# @CpuInstanceProperties
 #
 # List of properties to be used for hotplugging a CPU instance,
 # it should be passed by management with device_add command when
diff --git a/qapi/block-core.json b/qapi/block-core.json
index 39cdaba..d98fe73 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -1144,7 +1144,7 @@
   'data': 'DriveMirror' }
 
 ##
-# DriveMirror
+# @DriveMirror
 #
 # A set of parameters describing drive mirror setup.
 #
@@ -1368,7 +1368,7 @@
   'data': 'BlockIOThrottle' }
 
 ##
-# BlockIOThrottle
+# @BlockIOThrottle
 #
 # A set of parameters describing block throttling.
 #
diff --git a/qapi/crypto.json b/qapi/crypto.json
index 5c9d7d4..15d296e 100644
--- a/qapi/crypto.json
+++ b/qapi/crypto.json
@@ -3,7 +3,7 @@
 # QAPI crypto definitions
 
 ##
-# QCryptoTLSCredsEndpoint:
+# @QCryptoTLSCredsEndpoint:
 #
 # The type of network endpoint that will be using the credentials.
 # Most types of credential require different setup / structures
@@ -22,7 +22,7 @@
 
 
 ##
-# QCryptoSecretFormat:
+# @QCryptoSecretFormat:
 #
 # The data format that the secret is provided in
 #
@@ -36,7 +36,7 @@
 
 
 ##
-# QCryptoHashAlgorithm:
+# @QCryptoHashAlgorithm:
 #
 # The supported algorithms for computing content digests
 #
@@ -55,7 +55,7 @@
 
 
 ##
-# QCryptoCipherAlgorithm:
+# @QCryptoCipherAlgorithm:
 #
 # The supported algorithms for content encryption ciphers
 #
@@ -82,7 +82,7 @@
 
 
 ##
-# QCryptoCipherMode:
+# @QCryptoCipherMode:
 #
 # The supported modes for content encryption ciphers
 #
@@ -98,7 +98,7 @@
 
 
 ##
-# QCryptoIVGenAlgorithm:
+# @QCryptoIVGenAlgorithm:
 #
 # The supported algorithms for generating initialization
 # vectors for full disk encryption. The 'plain' generator
@@ -116,7 +116,7 @@
   'data': ['plain', 'plain64', 'essiv']}
 
 ##
-# QCryptoBlockFormat:
+# @QCryptoBlockFormat:
 #
 # The supported full disk encryption formats
 #
@@ -131,7 +131,7 @@
   'data': ['qcow', 'luks']}
 
 ##
-# QCryptoBlockOptionsBase:
+# @QCryptoBlockOptionsBase:
 #
 # The common options that apply to all full disk
 # encryption formats
@@ -144,7 +144,7 @@
   'data': { 'format': 'QCryptoBlockFormat' }}
 
 ##
-# QCryptoBlockOptionsQCow:
+# @QCryptoBlockOptionsQCow:
 #
 # The options that apply to QCow/QCow2 AES-CBC encryption format
 #
@@ -158,7 +158,7 @@
   'data': { '*key-secret': 'str' }}
 
 ##
-# QCryptoBlockOptionsLUKS:
+# @QCryptoBlockOptionsLUKS:
 #
 # The options that apply to LUKS encryption format
 #
@@ -172,7 +172,7 @@
 
 
 ##
-# QCryptoBlockCreateOptionsLUKS:
+# @QCryptoBlockCreateOptionsLUKS:
 #
 # The options that apply to LUKS encryption format initialization
 #
@@ -202,7 +202,7 @@
 
 
 ##
-# QCryptoBlockOpenOptions:
+# @QCryptoBlockOpenOptions:
 #
 # The options that are available for all encryption formats
 # when opening an existing volume
@@ -217,7 +217,7 @@
 
 
 ##
-# QCryptoBlockCreateOptions:
+# @QCryptoBlockCreateOptions:
 #
 # The options that are available for all encryption formats
 # when initializing a new volume
@@ -232,7 +232,7 @@
 
 
 ##
-# QCryptoBlockInfoBase:
+# @QCryptoBlockInfoBase:
 #
 # The common information that applies to all full disk
 # encryption formats
@@ -246,7 +246,7 @@
 
 
 ##
-# QCryptoBlockInfoLUKSSlot:
+# @QCryptoBlockInfoLUKSSlot:
 #
 # Information about the LUKS block encryption key
 # slot options
@@ -266,7 +266,7 @@
 
 
 ##
-# QCryptoBlockInfoLUKS:
+# @QCryptoBlockInfoLUKS:
 #
 # Information about the LUKS block encryption options
 #
@@ -294,7 +294,7 @@
            'slots': [ 'QCryptoBlockInfoLUKSSlot' ] }}
 
 ##
-# QCryptoBlockInfoQCow:
+# @QCryptoBlockInfoQCow:
 #
 # Information about the QCow block encryption options
 #
@@ -305,7 +305,7 @@
 
 
 ##
-# QCryptoBlockInfo:
+# @QCryptoBlockInfo:
 #
 # Information about the block encryption options
 #
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [Qemu-devel] [PATCH v5 06/17] qapi: fix various symbols mismatch in documentation
  2016-11-17 15:54 [Qemu-devel] [PATCH v5 00/17] qapi doc generation (whole version, squashed) Marc-André Lureau
                   ` (4 preceding siblings ...)
  2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 05/17] qapi: fix missing symbol @prefix Marc-André Lureau
@ 2016-11-17 15:54 ` Marc-André Lureau
  2016-11-17 17:40   ` Markus Armbruster
  2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 07/17] qapi: use one symbol per line Marc-André Lureau
                   ` (11 subsequent siblings)
  17 siblings, 1 reply; 48+ messages in thread
From: Marc-André Lureau @ 2016-11-17 15:54 UTC (permalink / raw)
  To: qemu-devel; +Cc: eblake, armbru, Marc-André Lureau

There are various mismatch:
- invalid symbols
- section and member symbols mismatch
- enum or union values vs 'type'

The documentation parser catches all these cases.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 qapi-schema.json     | 20 +++++++++-----------
 qapi/block-core.json |  4 ----
 qapi/common.json     |  6 +++---
 qapi/rocker.json     |  2 +-
 qga/qapi-schema.json |  6 +++---
 5 files changed, 16 insertions(+), 22 deletions(-)

diff --git a/qapi-schema.json b/qapi-schema.json
index 09dbc88..6ea6e2e 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -693,8 +693,6 @@
 #
 # Set various migration parameters.  See MigrationParameters for details.
 #
-# @x-checkpoint-delay: the delay time between two checkpoints. (Since 2.8)
-#
 # Since: 2.4
 ##
 { 'command': 'migrate-set-parameters', 'boxed': true,
@@ -1171,7 +1169,7 @@
            '*service': 'str', '*auth': 'str', '*clients': ['VncClientInfo']} }
 
 ##
-# @VncPriAuth:
+# @VncPrimaryAuth:
 #
 # vnc primary authentication method.
 #
@@ -3174,7 +3172,7 @@
 #
 # @alias: #optional an alias for the machine name
 #
-# @default: #optional whether the machine is default
+# @is-default: #optional whether the machine is default
 #
 # @cpu-max: maximum number of CPUs supported by the machine type
 #           (since 1.5.0)
@@ -3727,7 +3725,6 @@
 #
 # @device: The name of the special file for the device,
 #          i.e. /dev/ttyS0 on Unix or COM1: on Windows
-# @type: What kind of device this is.
 #
 # Since: 1.4
 ##
@@ -3992,7 +3989,7 @@
 #
 # A union referencing different TPM backend types' configuration options
 #
-# @passthrough: The configuration options for the TPM passthrough type
+# @type: 'passthrough' The configuration options for the TPM passthrough type
 #
 # Since: 1.5
 ##
@@ -4000,7 +3997,7 @@
    'data': { 'passthrough' : 'TPMPassthroughOptions' } }
 
 ##
-# @TpmInfo:
+# @TPMInfo:
 #
 # Information about the TPM
 #
@@ -4344,10 +4341,11 @@
 #
 # Input event union.
 #
-# @key: Input event of Keyboard
-# @btn: Input event of pointer buttons
-# @rel: Input event of relative pointer motion
-# @abs: Input event of absolute pointer motion
+# @type: the input type, one of:
+#  - 'key': Input event of Keyboard
+#  - 'btn': Input event of pointer buttons
+#  - 'rel': Input event of relative pointer motion
+#  - 'abs': Input event of absolute pointer motion
 #
 # Since: 2.0
 ##
diff --git a/qapi/block-core.json b/qapi/block-core.json
index d98fe73..33bc93a 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -2160,10 +2160,6 @@
 #
 # @type:       Transport type used for gluster connection
 #
-# @unix:       socket file
-#
-# @tcp:        host address and port number
-#
 # This is similar to SocketAddress, only distinction:
 #
 # 1. GlusterServer is a flat union, SocketAddress is a simple union.
diff --git a/qapi/common.json b/qapi/common.json
index 9353a7b..6987100 100644
--- a/qapi/common.json
+++ b/qapi/common.json
@@ -34,11 +34,11 @@
 #
 # A three-part version number.
 #
-# @qemu.major:  The major version number.
+# @major:  The major version number.
 #
-# @qemu.minor:  The minor version number.
+# @minor:  The minor version number.
 #
-# @qemu.micro:  The micro version number.
+# @micro:  The micro version number.
 #
 # Since: 2.4
 ##
diff --git a/qapi/rocker.json b/qapi/rocker.json
index 2fe7fdf..ace2776 100644
--- a/qapi/rocker.json
+++ b/qapi/rocker.json
@@ -1,5 +1,5 @@
 ##
-# @Rocker:
+# @RockerSwitch:
 #
 # Rocker switch information.
 #
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index 5173c4a..2e6cc91 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -203,7 +203,7 @@
 #
 # Open a file in the guest and retrieve a file handle for it
 #
-# @filepath: Full path to the file in the guest to open.
+# @path: Full path to the file in the guest to open.
 #
 # @mode: #optional open mode, as per fopen(), "r" is the default.
 #
@@ -378,7 +378,7 @@
   'data': { 'handle': 'int' } }
 
 ##
-# @GuestFsFreezeStatus
+# @GuestFsfreezeStatus
 #
 # An enumeration of filesystem freeze states
 #
@@ -766,7 +766,7 @@
 # @GuestDiskAddress:
 #
 # @pci-controller: controller's PCI address
-# @type: bus type
+# @bus-type: bus type
 # @bus: bus id
 # @target: target id
 # @unit: unit id
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [Qemu-devel] [PATCH v5 07/17] qapi: use one symbol per line
  2016-11-17 15:54 [Qemu-devel] [PATCH v5 00/17] qapi doc generation (whole version, squashed) Marc-André Lureau
                   ` (5 preceding siblings ...)
  2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 06/17] qapi: fix various symbols mismatch in documentation Marc-André Lureau
@ 2016-11-17 15:54 ` Marc-André Lureau
  2016-11-17 17:40   ` Markus Armbruster
  2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 08/17] qapi: add missing colon-ending for section name Marc-André Lureau
                   ` (10 subsequent siblings)
  17 siblings, 1 reply; 48+ messages in thread
From: Marc-André Lureau @ 2016-11-17 15:54 UTC (permalink / raw)
  To: qemu-devel; +Cc: eblake, armbru, Marc-André Lureau

The documentation parser we are going to add only handles a single
symbol per line.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 qapi-schema.json     | 10 +++++++---
 qapi/block-core.json |  8 ++++++--
 2 files changed, 13 insertions(+), 5 deletions(-)

diff --git a/qapi-schema.json b/qapi-schema.json
index 6ea6e2e..2ea012e 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -3610,15 +3610,19 @@
 ##
 # @QKeyCode:
 #
+# @unmapped: since 2.0
+# @pause: since 2.0
+# @ro: since 2.4
+# @kp_comma: since 2.4
+# @kp_equals: since 2.6
+# @power: since 2.6
+#
 # An enumeration of key name.
 #
 # This is used by the send-key command.
 #
 # Since: 1.3.0
 #
-# 'unmapped' and 'pause' since 2.0
-# 'ro' and 'kp_comma' since 2.4
-# 'kp_equals' and 'power' since 2.6
 ##
 { 'enum': 'QKeyCode',
   'data': [ 'unmapped',
diff --git a/qapi/block-core.json b/qapi/block-core.json
index 33bc93a..96d0859 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -1712,9 +1712,13 @@
 #
 # Drivers that are supported in block device operations.
 #
-# @host_device, @host_cdrom: Since 2.1
+# @host_device: Since 2.1
+# @host_cdrom: Since 2.1
 # @gluster: Since 2.7
-# @nbd, @nfs, @replication, @ssh: Since 2.8
+# @nbd: Since 2.8
+# @nfs: Since 2.8
+# @replication: Since 2.8
+# @ssh: Since 2.8
 #
 # Since: 2.0
 ##
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [Qemu-devel] [PATCH v5 08/17] qapi: add missing colon-ending for section name
  2016-11-17 15:54 [Qemu-devel] [PATCH v5 00/17] qapi doc generation (whole version, squashed) Marc-André Lureau
                   ` (6 preceding siblings ...)
  2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 07/17] qapi: use one symbol per line Marc-André Lureau
@ 2016-11-17 15:54 ` Marc-André Lureau
  2016-11-17 17:41   ` Markus Armbruster
  2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 09/17] qapi: add some sections in docs Marc-André Lureau
                   ` (9 subsequent siblings)
  17 siblings, 1 reply; 48+ messages in thread
From: Marc-André Lureau @ 2016-11-17 15:54 UTC (permalink / raw)
  To: qemu-devel; +Cc: eblake, armbru, Marc-André Lureau

The documentation parser we are going to add expects a section name to
end with ':', otherwise the comment is treated as free-form text body.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 qapi-schema.json     | 300 +++++++++++++++++++++++++--------------------------
 qapi/block-core.json | 196 ++++++++++++++++-----------------
 qapi/block.json      |  16 +--
 qapi/common.json     |   8 +-
 qapi/event.json      |  58 +++++-----
 qapi/introspect.json |  28 ++---
 qapi/trace.json      |   8 +-
 qga/qapi-schema.json |  44 ++++----
 8 files changed, 329 insertions(+), 329 deletions(-)

diff --git a/qapi-schema.json b/qapi-schema.json
index 2ea012e..dfe68ba 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -66,7 +66,7 @@
   'data': ['discard', 'delay', 'merge', 'slew' ] }
 
 ##
-# @add_client
+# @add_client:
 #
 # Allow client connections for VNC, Spice and socket based
 # character devices to be passed in to QEMU via SCM_RIGHTS.
@@ -97,7 +97,7 @@
 #
 # @name: #optional The name of the guest
 #
-# Since 0.14.0
+# Since: 0.14.0
 ##
 { 'struct': 'NameInfo', 'data': {'*name': 'str'} }
 
@@ -108,7 +108,7 @@
 #
 # Returns: @NameInfo of the guest
 #
-# Since 0.14.0
+# Since: 0.14.0
 ##
 { 'command': 'query-name', 'returns': 'NameInfo' }
 
@@ -137,7 +137,7 @@
 { 'command': 'query-kvm', 'returns': 'KvmInfo' }
 
 ##
-# @RunState
+# @RunState:
 #
 # An enumeration of VM run states.
 #
@@ -235,7 +235,7 @@
 #
 # Returns: The @UuidInfo for the guest
 #
-# Since 0.14.0
+# Since: 0.14.0
 ##
 { 'command': 'query-uuid', 'returns': 'UuidInfo' }
 
@@ -382,7 +382,7 @@
 { 'command': 'query-events', 'returns': ['EventInfo'] }
 
 ##
-# @MigrationStats
+# @MigrationStats:
 #
 # Detailed migration status.
 #
@@ -396,7 +396,7 @@
 #
 # @skipped: number of skipped zero pages (since 1.5)
 #
-# @normal : number of normal pages (since 1.2)
+# @normal: number of normal pages (since 1.2)
 #
 # @normal-bytes: number of normal bytes sent (since 1.2)
 #
@@ -420,7 +420,7 @@
            'postcopy-requests' : 'int' } }
 
 ##
-# @XBZRLECacheStats
+# @XBZRLECacheStats:
 #
 # Detailed XBZRLE migration cache statistics
 #
@@ -474,7 +474,7 @@
             'active', 'postcopy-active', 'completed', 'failed', 'colo' ] }
 
 ##
-# @MigrationInfo
+# @MigrationInfo:
 #
 # Information about current migration process.
 #
@@ -534,7 +534,7 @@
            '*error-desc': 'str'} }
 
 ##
-# @query-migrate
+# @query-migrate:
 #
 # Returns information about current migration process.
 #
@@ -545,7 +545,7 @@
 { 'command': 'query-migrate', 'returns': 'MigrationInfo' }
 
 ##
-# @MigrationCapability
+# @MigrationCapability:
 #
 # Migration capabilities enumeration
 #
@@ -593,7 +593,7 @@
            'compress', 'events', 'postcopy-ram', 'x-colo'] }
 
 ##
-# @MigrationCapabilityStatus
+# @MigrationCapabilityStatus:
 #
 # Migration capability information
 #
@@ -607,7 +607,7 @@
   'data': { 'capability' : 'MigrationCapability', 'state' : 'bool' } }
 
 ##
-# @migrate-set-capabilities
+# @migrate-set-capabilities:
 #
 # Enable/Disable the following migration capabilities (like xbzrle)
 #
@@ -619,7 +619,7 @@
   'data': { 'capabilities': ['MigrationCapabilityStatus'] } }
 
 ##
-# @query-migrate-capabilities
+# @query-migrate-capabilities:
 #
 # Returns information about the current migration capabilities status
 #
@@ -630,7 +630,7 @@
 { 'command': 'query-migrate-capabilities', 'returns':   ['MigrationCapabilityStatus']}
 
 ##
-# @MigrationParameter
+# @MigrationParameter:
 #
 # Migration parameters enumeration
 #
@@ -689,7 +689,7 @@
            'downtime-limit', 'x-checkpoint-delay' ] }
 
 ##
-# @migrate-set-parameters
+# @migrate-set-parameters:
 #
 # Set various migration parameters.  See MigrationParameters for details.
 #
@@ -699,7 +699,7 @@
   'data': 'MigrationParameters' }
 
 ##
-# @MigrationParameters
+# @MigrationParameters:
 #
 # Optional members can be omitted on input ('migrate-set-parameters')
 # but most members will always be present on output
@@ -758,7 +758,7 @@
             '*x-checkpoint-delay': 'int'} }
 
 ##
-# @query-migrate-parameters
+# @query-migrate-parameters:
 #
 # Returns information about the current migration parameters
 #
@@ -770,7 +770,7 @@
   'returns': 'MigrationParameters' }
 
 ##
-# @client_migrate_info
+# @client_migrate_info:
 #
 # Set migration information for remote display.  This makes the server
 # ask the client to automatically reconnect using the new parameters
@@ -789,7 +789,7 @@
             '*tls-port': 'int', '*cert-subject': 'str' } }
 
 ##
-# @migrate-start-postcopy
+# @migrate-start-postcopy:
 #
 # Followup to a migration command to switch the migration to postcopy mode.
 # The postcopy-ram capability must be set before the original migration
@@ -800,7 +800,7 @@
 { 'command': 'migrate-start-postcopy' }
 
 ##
-# @COLOMessage
+# @COLOMessage:
 #
 # The message transmission between Primary side and Secondary side.
 #
@@ -826,7 +826,7 @@
             'vmstate-loaded' ] }
 
 ##
-# @COLOMode
+# @COLOMode:
 #
 # The colo mode
 #
@@ -842,7 +842,7 @@
   'data': [ 'unknown', 'primary', 'secondary'] }
 
 ##
-# @FailoverStatus
+# @FailoverStatus:
 #
 # An enumeration of COLO failover status
 #
@@ -860,7 +860,7 @@
   'data': [ 'none', 'require', 'active', 'completed'] }
 
 ##
-# @x-colo-lost-heartbeat
+# @x-colo-lost-heartbeat:
 #
 # Tell qemu that heartbeat is lost, request it to do takeover procedures.
 # If this command is sent to the PVM, the Primary side will exit COLO mode.
@@ -954,7 +954,7 @@
 #
 # @pc: the 64-bit instruction pointer
 #
-# Since 2.6
+# Since: 2.6
 ##
 { 'struct': 'CpuInfoX86', 'data': { 'pc': 'int' } }
 
@@ -967,7 +967,7 @@
 #
 # @npc: the NPC component of the instruction pointer
 #
-# Since 2.6
+# Since: 2.6
 ##
 { 'struct': 'CpuInfoSPARC', 'data': { 'pc': 'int', 'npc': 'int' } }
 
@@ -978,7 +978,7 @@
 #
 # @nip: the instruction pointer
 #
-# Since 2.6
+# Since: 2.6
 ##
 { 'struct': 'CpuInfoPPC', 'data': { 'nip': 'int' } }
 
@@ -989,7 +989,7 @@
 #
 # @PC: the instruction pointer
 #
-# Since 2.6
+# Since: 2.6
 ##
 { 'struct': 'CpuInfoMIPS', 'data': { 'PC': 'int' } }
 
@@ -1000,7 +1000,7 @@
 #
 # @PC: the instruction pointer
 #
-# Since 2.6
+# Since: 2.6
 ##
 { 'struct': 'CpuInfoTricore', 'data': { 'PC': 'int' } }
 
@@ -1009,7 +1009,7 @@
 #
 # No additional information is available about the virtual CPU
 #
-# Since 2.6
+# Since: 2.6
 #
 ##
 { 'struct': 'CpuInfoOther', 'data': { } }
@@ -1044,7 +1044,7 @@
 #
 # Returns a list of information about each iothread.
 #
-# Note this list excludes the QEMU main loop thread, which is not declared
+# Note: this list excludes the QEMU main loop thread, which is not declared
 # using the -object iothread command-line option.  It is always the main thread
 # of the process.
 #
@@ -1055,7 +1055,7 @@
 { 'command': 'query-iothreads', 'returns': ['IOThreadInfo'] }
 
 ##
-# @NetworkAddressFamily
+# @NetworkAddressFamily:
 #
 # The network address family
 #
@@ -1075,7 +1075,7 @@
   'data': [ 'ipv4', 'ipv6', 'unix', 'vsock', 'unknown' ] }
 
 ##
-# @VncBasicInfo
+# @VncBasicInfo:
 #
 # The basic information for vnc network connection
 #
@@ -1098,7 +1098,7 @@
             'websocket': 'bool' } }
 
 ##
-# @VncServerInfo
+# @VncServerInfo:
 #
 # The network connection information for server
 #
@@ -1248,7 +1248,7 @@
 { 'command': 'query-vnc-servers', 'returns': ['VncInfo2'] }
 
 ##
-# @SpiceBasicInfo
+# @SpiceBasicInfo:
 #
 # The basic information for SPICE network connection
 #
@@ -1266,7 +1266,7 @@
             'family': 'NetworkAddressFamily' } }
 
 ##
-# @SpiceServerInfo
+# @SpiceServerInfo:
 #
 # Information about a SPICE server
 #
@@ -1279,7 +1279,7 @@
   'data': { '*auth': 'str' } }
 
 ##
-# @SpiceChannel
+# @SpiceChannel:
 #
 # Information about a SPICE client channel.
 #
@@ -1304,7 +1304,7 @@
            'tls': 'bool'} }
 
 ##
-# @SpiceQueryMouseMode
+# @SpiceQueryMouseMode:
 #
 # An enumeration of Spice mouse states.
 #
@@ -1323,7 +1323,7 @@
   'data': [ 'client', 'server', 'unknown' ] }
 
 ##
-# @SpiceInfo
+# @SpiceInfo:
 #
 # Information about the SPICE session.
 #
@@ -1362,7 +1362,7 @@
            'mouse-mode': 'SpiceQueryMouseMode', '*channels': ['SpiceChannel']} }
 
 ##
-# @query-spice
+# @query-spice:
 #
 # Returns information about the current SPICE server
 #
@@ -1412,7 +1412,7 @@
 { 'struct': 'PciMemoryRange', 'data': {'base': 'int', 'limit': 'int'} }
 
 ##
-# @PciMemoryRegion
+# @PciMemoryRegion:
 #
 # Information about a PCI device I/O region.
 #
@@ -1621,7 +1621,7 @@
 { 'command': 'cpu', 'data': {'index': 'int'} }
 
 ##
-# @cpu-add
+# @cpu-add:
 #
 # Adds CPU with specified ID
 #
@@ -1629,7 +1629,7 @@
 #
 # Returns: Nothing on success
 #
-# Since 1.5
+# Since: 1.5
 ##
 { 'command': 'cpu-add', 'data': {'id': 'int'} }
 
@@ -1760,17 +1760,17 @@
 { 'command': 'balloon', 'data': {'value': 'int'} }
 
 ##
-# @Abort
+# @Abort:
 #
 # This action can be used to test transaction failure.
 #
 # Since: 1.6
-###
+##
 { 'struct': 'Abort',
   'data': { } }
 
 ##
-# @ActionCompletionMode
+# @ActionCompletionMode:
 #
 # An enumeration of Transactional completion modes.
 #
@@ -1790,12 +1790,12 @@
   'data': [ 'individual', 'grouped' ] }
 
 ##
-# @TransactionAction
+# @TransactionAction:
 #
 # A discriminated record of operations that can be performed with
 # @transaction.
 #
-# Since 1.1
+# Since: 1.1
 #
 # drive-backup since 1.6
 # abort since 1.6
@@ -1818,7 +1818,7 @@
    } }
 
 ##
-# @TransactionProperties
+# @TransactionProperties:
 #
 # Optional arguments to modify the behavior of a Transaction.
 #
@@ -1835,7 +1835,7 @@
 }
 
 ##
-# @transaction
+# @transaction:
 #
 # Executes a number of transactionable QMP commands atomically. If any
 # operation fails, then the entire set of actions will be abandoned and the
@@ -1855,7 +1855,7 @@
 # information on only one failed operation returned in an error condition, and
 # subsequent actions will not have been attempted.
 #
-# Since 1.1
+# Since: 1.1
 ##
 { 'command': 'transaction',
   'data': { 'actions': [ 'TransactionAction' ],
@@ -1892,7 +1892,7 @@
   'returns': 'str' }
 
 ##
-# @migrate_cancel
+# @migrate_cancel:
 #
 # Cancel the current executing migration process.
 #
@@ -1905,7 +1905,7 @@
 { 'command': 'migrate_cancel' }
 
 ##
-# @migrate_set_downtime
+# @migrate_set_downtime:
 #
 # Set maximum tolerated downtime for migration.
 #
@@ -1920,7 +1920,7 @@
 { 'command': 'migrate_set_downtime', 'data': {'value': 'number'} }
 
 ##
-# @migrate_set_speed
+# @migrate_set_speed:
 #
 # Set maximum speed for migration.
 #
@@ -1935,7 +1935,7 @@
 { 'command': 'migrate_set_speed', 'data': {'value': 'int'} }
 
 ##
-# @migrate-set-cache-size
+# @migrate-set-cache-size:
 #
 # Set XBZRLE cache size
 #
@@ -1951,7 +1951,7 @@
 { 'command': 'migrate-set-cache-size', 'data': {'value': 'int'} }
 
 ##
-# @query-migrate-cache-size
+# @query-migrate-cache-size:
 #
 # query XBZRLE cache size
 #
@@ -2216,7 +2216,7 @@
   'returns': [ 'DevicePropertyInfo' ] }
 
 ##
-# @migrate
+# @migrate:
 #
 # Migrates the current running guest to another Virtual Machine.
 #
@@ -2237,7 +2237,7 @@
   'data': {'uri': 'str', '*blk': 'bool', '*inc': 'bool', '*detach': 'bool' } }
 
 ##
-# @migrate-incoming
+# @migrate-incoming:
 #
 # Start an incoming migration, the qemu must have been started
 # with -incoming defer
@@ -2271,7 +2271,7 @@
 { 'command': 'xen-save-devices-state', 'data': {'filename': 'str'} }
 
 ##
-# @xen-set-global-dirty-log
+# @xen-set-global-dirty-log:
 #
 # Enable or disable the global dirty log mode.
 #
@@ -2363,7 +2363,7 @@
   'data': [ 'elf', 'kdump-zlib', 'kdump-lzo', 'kdump-snappy' ] }
 
 ##
-# @dump-guest-memory
+# @dump-guest-memory:
 #
 # Dump guest's memory to vmcore. It is a synchronous operation that can take
 # very long depending on the amount of guest memory.
@@ -2417,7 +2417,7 @@
             '*format': 'DumpGuestMemoryFormat'} }
 
 ##
-# @DumpStatus
+# @DumpStatus:
 #
 # Describe the status of a long-running background guest memory dump.
 #
@@ -2429,13 +2429,13 @@
 #
 # @failed: the last dump has failed.
 #
-# Since 2.6
+# Since: 2.6
 ##
 { 'enum': 'DumpStatus',
   'data': [ 'none', 'active', 'completed', 'failed' ] }
 
 ##
-# @DumpQueryResult
+# @DumpQueryResult:
 #
 # The result format for 'query-dump'.
 #
@@ -2445,7 +2445,7 @@
 #
 # @total: total bytes to be written in latest dump (uncompressed)
 #
-# Since 2.6
+# Since: 2.6
 ##
 { 'struct': 'DumpQueryResult',
   'data': { 'status': 'DumpStatus',
@@ -2453,7 +2453,7 @@
             'total': 'int' } }
 
 ##
-# @query-dump
+# @query-dump:
 #
 # Query latest dump status.
 #
@@ -2488,7 +2488,7 @@
   'returns': 'DumpGuestMemoryCapability' }
 
 ##
-# @dump-skeys
+# @dump-skeys:
 #
 # Dump guest's storage keys
 #
@@ -2575,17 +2575,17 @@
 { 'command': 'object-del', 'data': {'id': 'str'} }
 
 ##
-# @NetdevNoneOptions
+# @NetdevNoneOptions:
 #
 # Use it alone to have zero network devices.
 #
-# Since 1.2
+# Since: 1.2
 ##
 { 'struct': 'NetdevNoneOptions',
   'data': { } }
 
 ##
-# @NetLegacyNicOptions
+# @NetLegacyNicOptions:
 #
 # Create a new Network Interface Card.
 #
@@ -2599,7 +2599,7 @@
 #
 # @vectors: #optional number of MSI-x vectors, 0 to disable MSI-X
 #
-# Since 1.2
+# Since: 1.2
 ##
 { 'struct': 'NetLegacyNicOptions',
   'data': {
@@ -2610,18 +2610,18 @@
     '*vectors': 'uint32' } }
 
 ##
-# @String
+# @String:
 #
 # A fat type wrapping 'str', to be embedded in lists.
 #
-# Since 1.2
+# Since: 1.2
 ##
 { 'struct': 'String',
   'data': {
     'str': 'str' } }
 
 ##
-# @NetdevUserOptions
+# @NetdevUserOptions:
 #
 # Use the user mode network stack which requires no administrator privilege to
 # run.
@@ -2678,7 +2678,7 @@
 #
 # @guestfwd: #optional forward guest TCP connections
 #
-# Since 1.2
+# Since: 1.2
 ##
 { 'struct': 'NetdevUserOptions',
   'data': {
@@ -2704,7 +2704,7 @@
     '*guestfwd':  ['String'] } }
 
 ##
-# @NetdevTapOptions
+# @NetdevTapOptions:
 #
 # Connect the host TAP network interface name to the VLAN.
 #
@@ -2741,7 +2741,7 @@
 # @poll-us: #optional maximum number of microseconds that could
 # be spent on busy polling for tap (since 2.7)
 #
-# Since 1.2
+# Since: 1.2
 ##
 { 'struct': 'NetdevTapOptions',
   'data': {
@@ -2762,7 +2762,7 @@
     '*poll-us':    'uint32'} }
 
 ##
-# @NetdevSocketOptions
+# @NetdevSocketOptions:
 #
 # Connect the VLAN to a remote VLAN in another QEMU virtual machine using a TCP
 # socket connection.
@@ -2779,7 +2779,7 @@
 #
 # @udp: #optional UDP unicast address and port number
 #
-# Since 1.2
+# Since: 1.2
 ##
 { 'struct': 'NetdevSocketOptions',
   'data': {
@@ -2791,7 +2791,7 @@
     '*udp':       'str' } }
 
 ##
-# @NetdevL2TPv3Options
+# @NetdevL2TPv3Options:
 #
 # Connect the VLAN to Ethernet over L2TPv3 Static tunnel
 #
@@ -2827,7 +2827,7 @@
 # @offset: #optional additional offset - allows the insertion of
 #          additional application-specific data before the packet payload
 #
-# Since 2.1
+# Since: 2.1
 ##
 { 'struct': 'NetdevL2TPv3Options',
   'data': {
@@ -2847,7 +2847,7 @@
     '*offset':      'uint32' } }
 
 ##
-# @NetdevVdeOptions
+# @NetdevVdeOptions:
 #
 # Connect the VLAN to a vde switch running on the host.
 #
@@ -2859,7 +2859,7 @@
 #
 # @mode: #optional permissions for socket
 #
-# Since 1.2
+# Since: 1.2
 ##
 { 'struct': 'NetdevVdeOptions',
   'data': {
@@ -2869,7 +2869,7 @@
     '*mode':  'uint16' } }
 
 ##
-# @NetdevDumpOptions
+# @NetdevDumpOptions:
 #
 # Dump VLAN network traffic to a file.
 #
@@ -2878,7 +2878,7 @@
 #
 # @file: #optional dump file path (default is qemu-vlan0.pcap)
 #
-# Since 1.2
+# Since: 1.2
 ##
 { 'struct': 'NetdevDumpOptions',
   'data': {
@@ -2886,7 +2886,7 @@
     '*file': 'str' } }
 
 ##
-# @NetdevBridgeOptions
+# @NetdevBridgeOptions:
 #
 # Connect a host TAP network interface to a host bridge device.
 #
@@ -2894,7 +2894,7 @@
 #
 # @helper: #optional command to execute to configure bridge
 #
-# Since 1.2
+# Since: 1.2
 ##
 { 'struct': 'NetdevBridgeOptions',
   'data': {
@@ -2902,20 +2902,20 @@
     '*helper': 'str' } }
 
 ##
-# @NetdevHubPortOptions
+# @NetdevHubPortOptions:
 #
 # Connect two or more net clients through a software hub.
 #
 # @hubid: hub identifier number
 #
-# Since 1.2
+# Since: 1.2
 ##
 { 'struct': 'NetdevHubPortOptions',
   'data': {
     'hubid':     'int32' } }
 
 ##
-# @NetdevNetmapOptions
+# @NetdevNetmapOptions:
 #
 # Connect a client to a netmap-enabled NIC or to a VALE switch port
 #
@@ -2928,7 +2928,7 @@
 #
 # @devname: #optional path of the netmap device (default: '/dev/netmap').
 #
-# Since 2.0
+# Since: 2.0
 ##
 { 'struct': 'NetdevNetmapOptions',
   'data': {
@@ -2936,7 +2936,7 @@
     '*devname':    'str' } }
 
 ##
-# @NetdevVhostUserOptions
+# @NetdevVhostUserOptions:
 #
 # Vhost-user network backend
 #
@@ -2947,7 +2947,7 @@
 # @queues: #optional number of queues to be created for multiqueue vhost-user
 #          (default: 1) (Since 2.5)
 #
-# Since 2.1
+# Since: 2.1
 ##
 { 'struct': 'NetdevVhostUserOptions',
   'data': {
@@ -2956,18 +2956,18 @@
     '*queues':        'int' } }
 
 ##
-# @NetClientDriver
+# @NetClientDriver:
 #
 # Available netdev drivers.
 #
-# Since 2.7
+# Since: 2.7
 ##
 { 'enum': 'NetClientDriver',
   'data': [ 'none', 'nic', 'user', 'tap', 'l2tpv3', 'socket', 'vde', 'dump',
             'bridge', 'hubport', 'netmap', 'vhost-user' ] }
 
 ##
-# @Netdev
+# @Netdev:
 #
 # Captures the configuration of a network device.
 #
@@ -2975,7 +2975,7 @@
 #
 # @type: Specify the driver used for interpreting remaining arguments.
 #
-# Since 1.2
+# Since: 1.2
 #
 # 'l2tpv3' - since 2.1
 ##
@@ -2997,7 +2997,7 @@
     'vhost-user': 'NetdevVhostUserOptions' } }
 
 ##
-# @NetLegacy
+# @NetLegacy:
 #
 # Captures the configuration of a network device; legacy.
 #
@@ -3009,7 +3009,7 @@
 #
 # @opts: device type specific properties (legacy)
 #
-# Since 1.2
+# Since: 1.2
 ##
 { 'struct': 'NetLegacy',
   'data': {
@@ -3019,11 +3019,11 @@
     'opts':  'NetLegacyOptions' } }
 
 ##
-# @NetLegacyOptions
+# @NetLegacyOptions:
 #
 # Like Netdev, but for use only by the legacy command line options
 #
-# Since 1.2
+# Since: 1.2
 ##
 { 'union': 'NetLegacyOptions',
   'data': {
@@ -3040,7 +3040,7 @@
     'vhost-user': 'NetdevVhostUserOptions' } }
 
 ##
-# @NetFilterDirection
+# @NetFilterDirection:
 #
 # Indicates whether a netfilter is attached to a netdev's transmit queue or
 # receive queue or both.
@@ -3054,13 +3054,13 @@
 # @tx: the filter is attached to the transmit queue of the netdev,
 #      where it will receive packets sent by the netdev.
 #
-# Since 2.5
+# Since: 2.5
 ##
 { 'enum': 'NetFilterDirection',
   'data': [ 'all', 'rx', 'tx' ] }
 
 ##
-# @InetSocketAddress
+# @InetSocketAddress:
 #
 # Captures a socket address or address range in the Internet namespace.
 #
@@ -3076,7 +3076,7 @@
 # @ipv6: whether to accept IPv6 addresses, default try both IPv4 and IPv6
 #        #optional
 #
-# Since 1.3
+# Since: 1.3
 ##
 { 'struct': 'InetSocketAddress',
   'data': {
@@ -3087,30 +3087,30 @@
     '*ipv6': 'bool' } }
 
 ##
-# @UnixSocketAddress
+# @UnixSocketAddress:
 #
 # Captures a socket address in the local ("Unix socket") namespace.
 #
 # @path: filesystem path to use
 #
-# Since 1.3
+# Since: 1.3
 ##
 { 'struct': 'UnixSocketAddress',
   'data': {
     'path': 'str' } }
 
 ##
-# @VsockSocketAddress
+# @VsockSocketAddress:
 #
 # Captures a socket address in the vsock namespace.
 #
 # @cid: unique host identifier
 # @port: port
 #
-# Note that string types are used to allow for possible future hostname or
+# Note: string types are used to allow for possible future hostname or
 # service resolution support.
 #
-# Since 2.8
+# Since: 2.8
 ##
 { 'struct': 'VsockSocketAddress',
   'data': {
@@ -3118,11 +3118,11 @@
     'port': 'str' } }
 
 ##
-# @SocketAddress
+# @SocketAddress:
 #
 # Captures the address of a socket, which could also be a named file descriptor
 #
-# Since 1.3
+# Since: 1.3
 ##
 { 'union': 'SocketAddress',
   'data': {
@@ -3273,7 +3273,7 @@
             '*props': 'any' } }
 
 ##
-# @CpuModelExpansionType
+# @CpuModelExpansionType:
 #
 # An enumeration of CPU model expansion types.
 #
@@ -3296,7 +3296,7 @@
 
 
 ##
-# @CpuModelExpansionInfo
+# @CpuModelExpansionInfo:
 #
 # The result of a cpu model expansion.
 #
@@ -3370,7 +3370,7 @@
   'data': [ 'incompatible', 'identical', 'superset', 'subset' ] }
 
 ##
-# @CpuModelCompareInfo
+# @CpuModelCompareInfo:
 #
 # The result of a CPU model comparison.
 #
@@ -3434,7 +3434,7 @@
   'returns': 'CpuModelCompareInfo' }
 
 ##
-# @CpuModelBaselineInfo
+# @CpuModelBaselineInfo:
 #
 # The result of a CPU model baseline.
 #
@@ -3644,7 +3644,7 @@
             'kp_comma', 'kp_equals', 'power' ] }
 
 ##
-# @KeyValue
+# @KeyValue:
 #
 # Represents a keyboard key.
 #
@@ -4030,7 +4030,7 @@
 { 'command': 'query-tpm', 'returns': ['TPMInfo'] }
 
 ##
-# @AcpiTableOptions
+# @AcpiTableOptions:
 #
 # Specify an ACPI table on the command line to load.
 #
@@ -4073,7 +4073,7 @@
 #        ACPI table header. At least one file is required. This field excludes
 #        @file.
 #
-# Since 1.5
+# Since: 1.5
 ##
 { 'struct': 'AcpiTableOptions',
   'data': {
@@ -4101,7 +4101,7 @@
 # @size: accepts a number followed by an optional suffix (K)ilo,
 #        (M)ega, (G)iga, (T)era
 #
-# Since 1.5
+# Since: 1.5
 ##
 { 'enum': 'CommandLineParameterType',
   'data': ['string', 'boolean', 'number', 'size'] }
@@ -4119,7 +4119,7 @@
 #
 # @default: #optional default value string (since 2.1)
 #
-# Since 1.5
+# Since: 1.5
 ##
 { 'struct': 'CommandLineParameterInfo',
   'data': { 'name': 'str',
@@ -4136,7 +4136,7 @@
 #
 # @parameters: an array of @CommandLineParameterInfo
 #
-# Since 1.5
+# Since: 1.5
 ##
 { 'struct': 'CommandLineOptionInfo',
   'data': { 'option': 'str', 'parameters': ['CommandLineParameterInfo'] } }
@@ -4151,13 +4151,13 @@
 # Returns: list of @CommandLineOptionInfo for all options (or for the given
 #          @option).  Returns an error if the given @option doesn't exist.
 #
-# Since 1.5
+# Since: 1.5
 ##
 {'command': 'query-command-line-options', 'data': { '*option': 'str' },
  'returns': ['CommandLineOptionInfo'] }
 
 ##
-# @X86CPURegister32
+# @X86CPURegister32:
 #
 # A X86 32-bit register
 #
@@ -4167,7 +4167,7 @@
   'data': [ 'EAX', 'EBX', 'ECX', 'EDX', 'ESP', 'EBP', 'ESI', 'EDI' ] }
 
 ##
-# @X86CPUFeatureWordInfo
+# @X86CPUFeatureWordInfo:
 #
 # Information about a X86 CPU feature word
 #
@@ -4189,11 +4189,11 @@
             'features': 'int' } }
 
 ##
-# @DummyForceArrays
+# @DummyForceArrays:
 #
 # Not used by QMP; hack to let us use X86CPUFeatureWordInfoList internally
 #
-# Since 2.5
+# Since: 2.5
 ##
 { 'struct': 'DummyForceArrays',
   'data': { 'unused': ['X86CPUFeatureWordInfo'] } }
@@ -4243,7 +4243,7 @@
 #
 # @multicast-table: a list of multicast macaddr string
 #
-# Since 1.6
+# Since: 1.6
 ##
 { 'struct': 'RxFilterInfo',
   'data': {
@@ -4278,7 +4278,7 @@
   'returns': ['RxFilterInfo'] }
 
 ##
-# @InputButton
+# @InputButton:
 #
 # Button of a pointer input device (mouse, tablet).
 #
@@ -4288,7 +4288,7 @@
   'data'  : [ 'left', 'middle', 'right', 'wheel-up', 'wheel-down' ] }
 
 ##
-# @InputAxis
+# @InputAxis:
 #
 # Position axis of a pointer input device (mouse, tablet).
 #
@@ -4298,7 +4298,7 @@
   'data'  : [ 'x', 'y' ] }
 
 ##
-# @InputKeyEvent
+# @InputKeyEvent:
 #
 # Keyboard input event.
 #
@@ -4312,7 +4312,7 @@
               'down'    : 'bool' } }
 
 ##
-# @InputBtnEvent
+# @InputBtnEvent:
 #
 # Pointer button input event.
 #
@@ -4326,7 +4326,7 @@
               'down'    : 'bool' } }
 
 ##
-# @InputMoveEvent
+# @InputMoveEvent:
 #
 # Pointer motion input event.
 #
@@ -4341,7 +4341,7 @@
               'value'   : 'int' } }
 
 ##
-# @InputEvent
+# @InputEvent:
 #
 # Input event union.
 #
@@ -4360,7 +4360,7 @@
               'abs'     : 'InputMoveEvent' } }
 
 ##
-# @input-send-event
+# @input-send-event:
 #
 # Send input event(s) to guest.
 #
@@ -4390,18 +4390,18 @@
             'events' : [ 'InputEvent' ] } }
 
 ##
-# @NumaOptions
+# @NumaOptions:
 #
 # A discriminated record of NUMA options. (for OptsVisitor)
 #
-# Since 2.1
+# Since: 2.1
 ##
 { 'union': 'NumaOptions',
   'data': {
     'node': 'NumaNodeOptions' }}
 
 ##
-# @NumaNodeOptions
+# @NumaNodeOptions:
 #
 # Create a guest NUMA node. (for OptsVisitor)
 #
@@ -4427,7 +4427,7 @@
    '*memdev': 'str' }}
 
 ##
-# @HostMemPolicy
+# @HostMemPolicy:
 #
 # Host memory policy types
 #
@@ -4441,7 +4441,7 @@
 # @interleave: memory allocations are interleaved across the set
 #              of host nodes specified
 #
-# Since 2.1
+# Since: 2.1
 ##
 { 'enum': 'HostMemPolicy',
   'data': [ 'default', 'preferred', 'bind', 'interleave' ] }
@@ -4530,7 +4530,7 @@
 { 'union': 'MemoryDeviceInfo', 'data': {'dimm': 'PCDIMMDeviceInfo'} }
 
 ##
-# @query-memory-devices
+# @query-memory-devices:
 #
 # Lists available memory devices and their state
 #
@@ -4539,7 +4539,7 @@
 { 'command': 'query-memory-devices', 'returns': ['MemoryDeviceInfo'] }
 
 ##
-# @ACPISlotType
+# @ACPISlotType:
 #
 # @DIMM: memory slot
 # @CPU: logical CPU slot (since 2.7)
@@ -4547,7 +4547,7 @@
 { 'enum': 'ACPISlotType', 'data': [ 'DIMM', 'CPU' ] }
 
 ##
-# @ACPIOSTInfo
+# @ACPIOSTInfo:
 #
 # OSPM Status Indication for a device
 # For description of possible values of @source and @status fields
@@ -4573,7 +4573,7 @@
               'status': 'int' } }
 
 ##
-# @query-acpi-ospm-status
+# @query-acpi-ospm-status:
 #
 # Lists ACPI OSPM status of ACPI device objects,
 # which might be reported via _OST method
@@ -4583,7 +4583,7 @@
 { 'command': 'query-acpi-ospm-status', 'returns': ['ACPIOSTInfo'] }
 
 ##
-# @WatchdogExpirationAction
+# @WatchdogExpirationAction:
 #
 # An enumeration of the actions taken when the watchdog device's timer is
 # expired
@@ -4611,7 +4611,7 @@
             'inject-nmi' ] }
 
 ##
-# @IoOperationType
+# @IoOperationType:
 #
 # An enumeration of the I/O operation types
 #
@@ -4625,7 +4625,7 @@
   'data': [ 'read', 'write' ] }
 
 ##
-# @GuestPanicAction
+# @GuestPanicAction:
 #
 # An enumeration of the actions taken when guest OS panic is detected
 #
@@ -4637,7 +4637,7 @@
   'data': [ 'pause', 'poweroff' ] }
 
 ##
-# @rtc-reset-reinjection
+# @rtc-reset-reinjection:
 #
 # This command will reset the RTC interrupt reinjection backlog.
 # Can be used if another mechanism to synchronize guest time
@@ -4720,7 +4720,7 @@
 { 'command': 'query-gic-capabilities', 'returns': ['GICCapability'] }
 
 ##
-# @CpuInstanceProperties
+# @CpuInstanceProperties:
 #
 # List of properties to be used for hotplugging a CPU instance,
 # it should be passed by management with device_add command when
@@ -4748,7 +4748,7 @@
 }
 
 ##
-# @HotpluggableCPU
+# @HotpluggableCPU:
 #
 # @type: CPU object type for usage with device_add command
 # @props: list of properties to be used for hotplugging CPU
@@ -4767,7 +4767,7 @@
 }
 
 ##
-# @query-hotpluggable-cpus
+# @query-hotpluggable-cpus:
 #
 # Returns: a list of HotpluggableCPU objects.
 #
diff --git a/qapi/block-core.json b/qapi/block-core.json
index 96d0859..ec1da2a 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -6,7 +6,7 @@
 { 'include': 'common.json' }
 
 ##
-# @SnapshotInfo
+# @SnapshotInfo:
 #
 # @id: unique snapshot id
 #
@@ -213,7 +213,7 @@
            '*filename': 'str' } }
 
 ##
-# @BlockdevCacheInfo
+# @BlockdevCacheInfo:
 #
 # Cache mode information for a block device
 #
@@ -378,7 +378,7 @@
 # @offset: if present, the image file stores the data for this range in
 #          raw format at the given offset.
 #
-# Since 1.7
+# Since: 1.7
 ##
 { 'struct': 'BlockDeviceMapEntry',
   'data': { 'start': 'int', 'length': 'int', 'depth': 'int', 'zero': 'bool',
@@ -790,7 +790,7 @@
                                       '*node-name': 'str', 'password': 'str'} }
 
 ##
-# @block_resize
+# @block_resize:
 #
 # Resize a block image while a guest is running.
 #
@@ -812,7 +812,7 @@
                                        'size': 'int' }}
 
 ##
-# @NewImageMode
+# @NewImageMode:
 #
 # An enumeration that tells QEMU how to set the backing file path in
 # a new image file.
@@ -829,7 +829,7 @@
   'data': [ 'existing', 'absolute-paths' ] }
 
 ##
-# @BlockdevSnapshotSync
+# @BlockdevSnapshotSync:
 #
 # Either @device or @node-name must be set but not both.
 #
@@ -852,7 +852,7 @@
             '*format': 'str', '*mode': 'NewImageMode' } }
 
 ##
-# @BlockdevSnapshot
+# @BlockdevSnapshot:
 #
 # @node: device or node name that will have a snapshot created.
 #
@@ -861,13 +861,13 @@
 #           It must not have a current backing file (this can be
 #           achieved by passing "backing": "" to blockdev-add).
 #
-# Since 2.5
+# Since: 2.5
 ##
 { 'struct': 'BlockdevSnapshot',
   'data': { 'node': 'str', 'overlay': 'str' } }
 
 ##
-# @DriveBackup
+# @DriveBackup:
 #
 # @job-id: #optional identifier for the newly-created block job. If
 #          omitted, the device name will be used. (Since 2.7)
@@ -905,9 +905,9 @@
 #                   default 'report' (no limitations, since this applies to
 #                   a different block device than @device).
 #
-# Note that @on-source-error and @on-target-error only affect background I/O.
-# If an error occurs during a guest write request, the device's rerror/werror
-# actions will be used.
+# Note: @on-source-error and @on-target-error only affect background
+# I/O.  If an error occurs during a guest write request, the device's
+# rerror/werror actions will be used.
 #
 # Since: 1.6
 ##
@@ -919,7 +919,7 @@
             '*on-target-error': 'BlockdevOnError' } }
 
 ##
-# @BlockdevBackup
+# @BlockdevBackup:
 #
 # @job-id: #optional identifier for the newly-created block job. If
 #          omitted, the device name will be used. (Since 2.7)
@@ -946,9 +946,9 @@
 #                   default 'report' (no limitations, since this applies to
 #                   a different block device than @device).
 #
-# Note that @on-source-error and @on-target-error only affect background I/O.
-# If an error occurs during a guest write request, the device's rerror/werror
-# actions will be used.
+# Note: @on-source-error and @on-target-error only affect background
+# I/O.  If an error occurs during a guest write request, the device's
+# rerror/werror actions will be used.
 #
 # Since: 2.3
 ##
@@ -961,7 +961,7 @@
             '*on-target-error': 'BlockdevOnError' } }
 
 ##
-# @blockdev-snapshot-sync
+# @blockdev-snapshot-sync:
 #
 # Generates a synchronous snapshot of a block device.
 #
@@ -970,26 +970,26 @@
 # Returns: nothing on success
 #          If @device is not a valid block device, DeviceNotFound
 #
-# Since 0.14.0
+# Since: 0.14.0
 ##
 { 'command': 'blockdev-snapshot-sync',
   'data': 'BlockdevSnapshotSync' }
 
 
 ##
-# @blockdev-snapshot
+# @blockdev-snapshot:
 #
 # Generates a snapshot of a block device.
 #
 # For the arguments, see the documentation of BlockdevSnapshot.
 #
-# Since 2.5
+# Since: 2.5
 ##
 { 'command': 'blockdev-snapshot',
   'data': 'BlockdevSnapshot' }
 
 ##
-# @change-backing-file
+# @change-backing-file:
 #
 # Change the backing file in the image file metadata.  This does not
 # cause QEMU to reopen the image file to reparse the backing filename
@@ -1016,7 +1016,7 @@
             'backing-file': 'str' } }
 
 ##
-# @block-commit
+# @block-commit:
 #
 # Live commit of data from overlay image nodes into backing nodes - i.e.,
 # writes data between 'top' and 'base' into 'base'.
@@ -1079,7 +1079,7 @@
             '*backing-file': 'str', '*speed': 'int' } }
 
 ##
-# @drive-backup
+# @drive-backup:
 #
 # Start a point-in-time copy of a block device to a new destination.  The
 # status of ongoing drive-backup operations can be checked with
@@ -1092,13 +1092,13 @@
 # Returns: nothing on success
 #          If @device is not a valid block device, GenericError
 #
-# Since 1.6
+# Since: 1.6
 ##
 { 'command': 'drive-backup', 'boxed': true,
   'data': 'DriveBackup' }
 
 ##
-# @blockdev-backup
+# @blockdev-backup:
 #
 # Start a point-in-time copy of a block device to a new destination.  The
 # status of ongoing blockdev-backup operations can be checked with
@@ -1111,25 +1111,25 @@
 # Returns: nothing on success
 #          If @device is not a valid block device, DeviceNotFound
 #
-# Since 2.3
+# Since: 2.3
 ##
 { 'command': 'blockdev-backup', 'boxed': true,
   'data': 'BlockdevBackup' }
 
 
 ##
-# @query-named-block-nodes
+# @query-named-block-nodes:
 #
 # Get the named block driver list
 #
 # Returns: the list of BlockDeviceInfo
 #
-# Since 2.0
+# Since: 2.0
 ##
 { 'command': 'query-named-block-nodes', 'returns': [ 'BlockDeviceInfo' ] }
 
 ##
-# @drive-mirror
+# @drive-mirror:
 #
 # Start mirroring a block device's writes to a new destination.
 #
@@ -1138,13 +1138,13 @@
 # Returns: nothing on success
 #          If @device is not a valid block device, GenericError
 #
-# Since 1.3
+# Since: 1.3
 ##
 { 'command': 'drive-mirror', 'boxed': true,
   'data': 'DriveMirror' }
 
 ##
-# @DriveMirror
+# @DriveMirror:
 #
 # A set of parameters describing drive mirror setup.
 #
@@ -1198,7 +1198,7 @@
 #         written. Both will result in identical contents.
 #         Default is true. (Since 2.4)
 #
-# Since 1.3
+# Since: 1.3
 ##
 { 'struct': 'DriveMirror',
   'data': { '*job-id': 'str', 'device': 'str', 'target': 'str',
@@ -1210,19 +1210,19 @@
             '*unmap': 'bool' } }
 
 ##
-# @BlockDirtyBitmap
+# @BlockDirtyBitmap:
 #
 # @node: name of device/node which the bitmap is tracking
 #
 # @name: name of the dirty bitmap
 #
-# Since 2.4
+# Since: 2.4
 ##
 { 'struct': 'BlockDirtyBitmap',
   'data': { 'node': 'str', 'name': 'str' } }
 
 ##
-# @BlockDirtyBitmapAdd
+# @BlockDirtyBitmapAdd:
 #
 # @node: name of device/node which the bitmap is tracking
 #
@@ -1231,13 +1231,13 @@
 # @granularity: #optional the bitmap granularity, default is 64k for
 #               block-dirty-bitmap-add
 #
-# Since 2.4
+# Since: 2.4
 ##
 { 'struct': 'BlockDirtyBitmapAdd',
   'data': { 'node': 'str', 'name': 'str', '*granularity': 'uint32' } }
 
 ##
-# @block-dirty-bitmap-add
+# @block-dirty-bitmap-add:
 #
 # Create a dirty bitmap with a name on the node
 #
@@ -1245,13 +1245,13 @@
 #          If @node is not a valid block device or node, DeviceNotFound
 #          If @name is already taken, GenericError with an explanation
 #
-# Since 2.4
+# Since: 2.4
 ##
 { 'command': 'block-dirty-bitmap-add',
   'data': 'BlockDirtyBitmapAdd' }
 
 ##
-# @block-dirty-bitmap-remove
+# @block-dirty-bitmap-remove:
 #
 # Remove a dirty bitmap on the node
 #
@@ -1260,13 +1260,13 @@
 #          If @name is not found, GenericError with an explanation
 #          if @name is frozen by an operation, GenericError
 #
-# Since 2.4
+# Since: 2.4
 ##
 { 'command': 'block-dirty-bitmap-remove',
   'data': 'BlockDirtyBitmap' }
 
 ##
-# @block-dirty-bitmap-clear
+# @block-dirty-bitmap-clear:
 #
 # Clear (reset) a dirty bitmap on the device
 #
@@ -1274,13 +1274,13 @@
 #          If @node is not a valid block device, DeviceNotFound
 #          If @name is not found, GenericError with an explanation
 #
-# Since 2.4
+# Since: 2.4
 ##
 { 'command': 'block-dirty-bitmap-clear',
   'data': 'BlockDirtyBitmap' }
 
 ##
-# @blockdev-mirror
+# @blockdev-mirror:
 #
 # Start mirroring a block device's writes to a new destination.
 #
@@ -1321,7 +1321,7 @@
 #
 # Returns: nothing on success.
 #
-# Since 2.6
+# Since: 2.6
 ##
 { 'command': 'blockdev-mirror',
   'data': { '*job-id': 'str', 'device': 'str', 'target': 'str',
@@ -1368,7 +1368,7 @@
   'data': 'BlockIOThrottle' }
 
 ##
-# @BlockIOThrottle
+# @BlockIOThrottle:
 #
 # A set of parameters describing block throttling.
 #
@@ -1650,7 +1650,7 @@
 { 'command': 'block-job-complete', 'data': { 'device': 'str' } }
 
 ##
-# @BlockdevDiscardOptions
+# @BlockdevDiscardOptions:
 #
 # Determines how to handle discard requests.
 #
@@ -1663,7 +1663,7 @@
   'data': [ 'ignore', 'unmap' ] }
 
 ##
-# @BlockdevDetectZeroesOptions
+# @BlockdevDetectZeroesOptions:
 #
 # Describes the operation mode for the automatic conversion of plain
 # zero writes by the OS to driver specific optimized zero write commands.
@@ -1679,7 +1679,7 @@
   'data': [ 'off', 'on', 'unmap' ] }
 
 ##
-# @BlockdevAioOptions
+# @BlockdevAioOptions:
 #
 # Selects the AIO backend to handle I/O requests
 #
@@ -1692,7 +1692,7 @@
   'data': [ 'threads', 'native' ] }
 
 ##
-# @BlockdevCacheOptions
+# @BlockdevCacheOptions:
 #
 # Includes cache-related options for block devices
 #
@@ -1708,7 +1708,7 @@
             '*no-flush': 'bool' } }
 
 ##
-# @BlockdevDriver
+# @BlockdevDriver:
 #
 # Drivers that are supported in block device operations.
 #
@@ -1731,7 +1731,7 @@
             'vvfat' ] }
 
 ##
-# @BlockdevOptionsFile
+# @BlockdevOptionsFile:
 #
 # Driver specific block device options for the file backend.
 #
@@ -1745,7 +1745,7 @@
             '*aio': 'BlockdevAioOptions' } }
 
 ##
-# @BlockdevOptionsNull
+# @BlockdevOptionsNull:
 #
 # Driver specific block device options for the null backend.
 #
@@ -1760,7 +1760,7 @@
   'data': { '*size': 'int', '*latency-ns': 'uint64' } }
 
 ##
-# @BlockdevOptionsVVFAT
+# @BlockdevOptionsVVFAT:
 #
 # Driver specific block device options for the vvfat protocol.
 #
@@ -1781,7 +1781,7 @@
             '*label': 'str', '*rw': 'bool' } }
 
 ##
-# @BlockdevOptionsGenericFormat
+# @BlockdevOptionsGenericFormat:
 #
 # Driver specific block device options for image format that have no option
 # besides their data source.
@@ -1794,7 +1794,7 @@
   'data': { 'file': 'BlockdevRef' } }
 
 ##
-# @BlockdevOptionsLUKS
+# @BlockdevOptionsLUKS:
 #
 # Driver specific block device options for LUKS.
 #
@@ -1810,7 +1810,7 @@
 
 
 ##
-# @BlockdevOptionsGenericCOWFormat
+# @BlockdevOptionsGenericCOWFormat:
 #
 # Driver specific block device options for image format that have no option
 # besides their data source and an optional backing file.
@@ -1827,7 +1827,7 @@
   'data': { '*backing': 'BlockdevRef' } }
 
 ##
-# @Qcow2OverlapCheckMode
+# @Qcow2OverlapCheckMode:
 #
 # General overlap check modes.
 #
@@ -1847,7 +1847,7 @@
   'data': [ 'none', 'constant', 'cached', 'all' ] }
 
 ##
-# @Qcow2OverlapCheckFlags
+# @Qcow2OverlapCheckFlags:
 #
 # Structure of flags for each metadata structure. Setting a field to 'true'
 # makes qemu guard that structure against unintended overwriting. The default
@@ -1870,7 +1870,7 @@
             '*inactive-l2':    'bool' } }
 
 ##
-# @Qcow2OverlapChecks
+# @Qcow2OverlapChecks:
 #
 # Specifies which metadata structures should be guarded against unintended
 # overwriting.
@@ -1887,7 +1887,7 @@
             'mode':  'Qcow2OverlapCheckMode' } }
 
 ##
-# @BlockdevOptionsQcow2
+# @BlockdevOptionsQcow2:
 #
 # Driver specific block device options for qcow2.
 #
@@ -1937,7 +1937,7 @@
 
 
 ##
-# @BlockdevOptionsArchipelago
+# @BlockdevOptionsArchipelago:
 #
 # Driver specific block device options for Archipelago.
 #
@@ -1966,7 +1966,7 @@
             '*segment': 'str' } }
 
 ##
-# @BlockdevOptionsSsh
+# @BlockdevOptionsSsh:
 #
 # @server:              host address
 #
@@ -1977,7 +1977,7 @@
 #
 # TODO: Expose the host_key_check option in QMP
 #
-# Since 2.8
+# Since: 2.8
 ##
 { 'struct': 'BlockdevOptionsSsh',
   'data': { 'server': 'InetSocketAddress',
@@ -1986,7 +1986,7 @@
 
 
 ##
-# @BlkdebugEvent
+# @BlkdebugEvent:
 #
 # Trigger events supported by blkdebug.
 #
@@ -2009,7 +2009,7 @@
             'pwritev_zero', 'pwritev_done', 'empty_image_prepare' ] }
 
 ##
-# @BlkdebugInjectErrorOptions
+# @BlkdebugInjectErrorOptions:
 #
 # Describes a single error injection for blkdebug.
 #
@@ -2041,7 +2041,7 @@
             '*immediately': 'bool' } }
 
 ##
-# @BlkdebugSetStateOptions
+# @BlkdebugSetStateOptions:
 #
 # Describes a single state-change event for blkdebug.
 #
@@ -2061,7 +2061,7 @@
             'new_state': 'int' } }
 
 ##
-# @BlockdevOptionsBlkdebug
+# @BlockdevOptionsBlkdebug:
 #
 # Driver specific block device options for blkdebug.
 #
@@ -2086,7 +2086,7 @@
             '*set-state': ['BlkdebugSetStateOptions'] } }
 
 ##
-# @BlockdevOptionsBlkverify
+# @BlockdevOptionsBlkverify:
 #
 # Driver specific block device options for blkverify.
 #
@@ -2101,7 +2101,7 @@
             'raw': 'BlockdevRef' } }
 
 ##
-# @QuorumReadPattern
+# @QuorumReadPattern:
 #
 # An enumeration of quorum read patterns.
 #
@@ -2114,7 +2114,7 @@
 { 'enum': 'QuorumReadPattern', 'data': [ 'quorum', 'fifo' ] }
 
 ##
-# @BlockdevOptionsQuorum
+# @BlockdevOptionsQuorum:
 #
 # Driver specific block device options for Quorum
 #
@@ -2141,7 +2141,7 @@
             '*read-pattern': 'QuorumReadPattern' } }
 
 ##
-# @GlusterTransport
+# @GlusterTransport:
 #
 # An enumeration of Gluster transport types
 #
@@ -2156,7 +2156,7 @@
 
 
 ##
-# @GlusterServer
+# @GlusterServer:
 #
 # Captures the address of a socket
 #
@@ -2185,7 +2185,7 @@
             'tcp': 'InetSocketAddress' } }
 
 ##
-# @BlockdevOptionsGluster
+# @BlockdevOptionsGluster:
 #
 # Driver specific block device options for Gluster
 #
@@ -2209,7 +2209,7 @@
             '*logfile': 'str' } }
 
 ##
-# @ReplicationMode
+# @ReplicationMode:
 #
 # An enumeration of replication modes.
 #
@@ -2222,7 +2222,7 @@
 { 'enum' : 'ReplicationMode', 'data' : [ 'primary', 'secondary' ] }
 
 ##
-# @BlockdevOptionsReplication
+# @BlockdevOptionsReplication:
 #
 # Driver specific block device options for replication
 #
@@ -2240,19 +2240,19 @@
             '*top-id': 'str' } }
 
 ##
-# @NFSTransport
+# @NFSTransport:
 #
 # An enumeration of NFS transport types
 #
 # @inet:        TCP transport
 #
-# Since 2.8
+# Since: 2.8
 ##
 { 'enum': 'NFSTransport',
   'data': [ 'inet' ] }
 
 ##
-# @NFSServer
+# @NFSServer:
 #
 # Captures the address of the socket
 #
@@ -2260,14 +2260,14 @@
 #
 # @host:        host address for NFS server
 #
-# Since 2.8
+# Since: 2.8
 ##
 { 'struct': 'NFSServer',
   'data': { 'type': 'NFSTransport',
             'host': 'str' } }
 
 ##
-# @BlockdevOptionsNfs
+# @BlockdevOptionsNfs:
 #
 # Driver specific block device option for NFS
 #
@@ -2295,7 +2295,7 @@
 # @debug-level:             #optional set the NFS debug level (max 2) (defaults
 #                           to libnfs default)
 #
-# Since 2.8
+# Since: 2.8
 ##
 { 'struct': 'BlockdevOptionsNfs',
   'data': { 'server': 'NFSServer',
@@ -2308,7 +2308,7 @@
             '*debug-level': 'int' } }
 
 ##
-# @BlockdevOptionsCurl
+# @BlockdevOptionsCurl:
 #
 # Driver specific block device options for the curl backend.
 #
@@ -2320,7 +2320,7 @@
   'data': { 'filename': 'str' } }
 
 ##
-# @BlockdevOptionsNbd
+# @BlockdevOptionsNbd:
 #
 # Driver specific block device options for NBD.
 #
@@ -2338,7 +2338,7 @@
             '*tls-creds': 'str' } }
 
 ##
-# @BlockdevOptionsRaw
+# @BlockdevOptionsRaw:
 #
 # Driver specific block device options for the raw driver.
 #
@@ -2352,7 +2352,7 @@
   'data': { '*offset': 'int', '*size': 'int' } }
 
 ##
-# @BlockdevOptions
+# @BlockdevOptions:
 #
 # Options for creating a block device.  Many options are available for all
 # block devices, independent of the block driver:
@@ -2418,7 +2418,7 @@
   } }
 
 ##
-# @BlockdevRef
+# @BlockdevRef:
 #
 # Reference to a block device.
 #
@@ -2616,7 +2616,7 @@
 
 
 ##
-# @BlockErrorAction
+# @BlockErrorAction:
 #
 # An enumeration of action that has been taken when a DISK I/O occurs
 #
@@ -2633,7 +2633,7 @@
 
 
 ##
-# @BLOCK_IMAGE_CORRUPTED
+# @BLOCK_IMAGE_CORRUPTED:
 #
 # Emitted when a corruption has been detected in a disk image
 #
@@ -2668,7 +2668,7 @@
             'fatal'      : 'bool' } }
 
 ##
-# @BLOCK_IO_ERROR
+# @BLOCK_IO_ERROR:
 #
 # Emitted when a disk I/O error occurs
 #
@@ -2704,7 +2704,7 @@
             'reason': 'str' } }
 
 ##
-# @BLOCK_JOB_COMPLETED
+# @BLOCK_JOB_COMPLETED:
 #
 # Emitted when a block job has completed
 #
@@ -2736,7 +2736,7 @@
             '*error': 'str' } }
 
 ##
-# @BLOCK_JOB_CANCELLED
+# @BLOCK_JOB_CANCELLED:
 #
 # Emitted when a block job has been cancelled
 #
@@ -2762,7 +2762,7 @@
             'speed' : 'int' } }
 
 ##
-# @BLOCK_JOB_ERROR
+# @BLOCK_JOB_ERROR:
 #
 # Emitted when a block job encounters an error
 #
@@ -2781,7 +2781,7 @@
             'action'   : 'BlockErrorAction' } }
 
 ##
-# @BLOCK_JOB_READY
+# @BLOCK_JOB_READY:
 #
 # Emitted when a block job is ready to complete
 #
@@ -2810,7 +2810,7 @@
             'speed' : 'int' } }
 
 ##
-# @PreallocMode
+# @PreallocMode:
 #
 # Preallocation mode of QEMU image file
 #
@@ -2822,13 +2822,13 @@
 #        space is really available. @full preallocation also sets up
 #        metadata correctly.
 #
-# Since 2.2
+# Since: 2.2
 ##
 { 'enum': 'PreallocMode',
   'data': [ 'off', 'metadata', 'falloc', 'full' ] }
 
 ##
-# @BLOCK_WRITE_THRESHOLD
+# @BLOCK_WRITE_THRESHOLD:
 #
 # Emitted when writes on block device reaches or exceeds the
 # configured write threshold. For thin-provisioned devices, this
@@ -2851,7 +2851,7 @@
             'write-threshold': 'uint64' } }
 
 ##
-# @block-set-write-threshold
+# @block-set-write-threshold:
 #
 # Change the write threshold for a block drive. An event will be delivered
 # if a write to this block drive crosses the configured threshold.
@@ -2869,7 +2869,7 @@
   'data': { 'node-name': 'str', 'write-threshold': 'uint64' } }
 
 ##
-# @x-blockdev-change
+# @x-blockdev-change:
 #
 # Dynamically reconfigure the block driver state graph. It can be used
 # to add, remove, insert or replace a graph node. Currently only the
diff --git a/qapi/block.json b/qapi/block.json
index 4661fc9..937df05 100644
--- a/qapi/block.json
+++ b/qapi/block.json
@@ -40,7 +40,7 @@
   'data': ['auto', 'none', 'lba', 'large', 'rechs']}
 
 ##
-# @FloppyDriveType
+# @FloppyDriveType:
 #
 # Type of Floppy drive to be emulated by the Floppy Disk Controller.
 #
@@ -56,7 +56,7 @@
   'data': ['144', '288', '120', 'none', 'auto']}
 
 ##
-# @BlockdevSnapshotInternal
+# @BlockdevSnapshotInternal:
 #
 # @device: the device name or node-name of a root node to generate the snapshot
 #          from
@@ -73,7 +73,7 @@
   'data': { 'device': 'str', 'name': 'str' } }
 
 ##
-# @blockdev-snapshot-internal-sync
+# @blockdev-snapshot-internal-sync:
 #
 # Synchronously take an internal snapshot of a block device, when the format
 # of the image used supports it.
@@ -87,13 +87,13 @@
 #          If the format of the image used does not support it,
 #          BlockFormatFeatureNotSupported
 #
-# Since 1.7
+# Since: 1.7
 ##
 { 'command': 'blockdev-snapshot-internal-sync',
   'data': 'BlockdevSnapshotInternal' }
 
 ##
-# @blockdev-snapshot-delete-internal-sync
+# @blockdev-snapshot-delete-internal-sync:
 #
 # Synchronously delete an internal snapshot of a block device, when the format
 # of the image used support it. The snapshot is identified by name or id or
@@ -114,7 +114,7 @@
 #          BlockFormatFeatureNotSupported
 #          If @id and @name are both not specified, GenericError
 #
-# Since 1.7
+# Since: 1.7
 ##
 { 'command': 'blockdev-snapshot-delete-internal-sync',
   'data': { 'device': 'str', '*id': 'str', '*name': 'str'},
@@ -190,7 +190,7 @@
 { 'command': 'nbd-server-stop' }
 
 ##
-# @DEVICE_TRAY_MOVED
+# @DEVICE_TRAY_MOVED:
 #
 # Emitted whenever the tray of a removable device is moved by the guest or by
 # HMP/QMP commands
@@ -209,7 +209,7 @@
   'data': { 'device': 'str', 'id': 'str', 'tray-open': 'bool' } }
 
 ##
-# @QuorumOpType
+# @QuorumOpType:
 #
 # An enumeration of the quorum operation types
 #
diff --git a/qapi/common.json b/qapi/common.json
index 6987100..624a861 100644
--- a/qapi/common.json
+++ b/qapi/common.json
@@ -3,7 +3,7 @@
 # QAPI common definitions
 
 ##
-# @QapiErrorClass
+# @QapiErrorClass:
 #
 # QEMU error classes
 #
@@ -30,7 +30,7 @@
             'DeviceNotActive', 'DeviceNotFound', 'KVMMissingCap' ] }
 
 ##
-# @VersionTriple
+# @VersionTriple:
 #
 # A three-part version number.
 #
@@ -101,7 +101,7 @@
 { 'command': 'query-commands', 'returns': ['CommandInfo'] }
 
 ##
-# @OnOffAuto
+# @OnOffAuto:
 #
 # An enumeration of three options: on, off, and auto
 #
@@ -117,7 +117,7 @@
   'data': [ 'auto', 'on', 'off' ] }
 
 ##
-# @OnOffSplit
+# @OnOffSplit:
 #
 # An enumeration of three values: on, off, and split
 #
diff --git a/qapi/event.json b/qapi/event.json
index 8642052..37bf34e 100644
--- a/qapi/event.json
+++ b/qapi/event.json
@@ -1,5 +1,5 @@
 ##
-# @SHUTDOWN
+# @SHUTDOWN:
 #
 # Emitted when the virtual machine has shut down, indicating that qemu is
 # about to exit.
@@ -12,7 +12,7 @@
 { 'event': 'SHUTDOWN' }
 
 ##
-# @POWERDOWN
+# @POWERDOWN:
 #
 # Emitted when the virtual machine is powered down through the power control
 # system, such as via ACPI.
@@ -22,7 +22,7 @@
 { 'event': 'POWERDOWN' }
 
 ##
-# @RESET
+# @RESET:
 #
 # Emitted when the virtual machine is reset
 #
@@ -31,7 +31,7 @@
 { 'event': 'RESET' }
 
 ##
-# @STOP
+# @STOP:
 #
 # Emitted when the virtual machine is stopped
 #
@@ -40,7 +40,7 @@
 { 'event': 'STOP' }
 
 ##
-# @RESUME
+# @RESUME:
 #
 # Emitted when the virtual machine resumes execution
 #
@@ -49,7 +49,7 @@
 { 'event': 'RESUME' }
 
 ##
-# @SUSPEND
+# @SUSPEND:
 #
 # Emitted when guest enters a hardware suspension state, for example, S3 state,
 # which is sometimes called standby state
@@ -59,7 +59,7 @@
 { 'event': 'SUSPEND' }
 
 ##
-# @SUSPEND_DISK
+# @SUSPEND_DISK:
 #
 # Emitted when guest enters a hardware suspension state with data saved on
 # disk, for example, S4 state, which is sometimes called hibernate state
@@ -71,7 +71,7 @@
 { 'event': 'SUSPEND_DISK' }
 
 ##
-# @WAKEUP
+# @WAKEUP:
 #
 # Emitted when the guest has woken up from suspend state and is running
 #
@@ -80,7 +80,7 @@
 { 'event': 'WAKEUP' }
 
 ##
-# @RTC_CHANGE
+# @RTC_CHANGE:
 #
 # Emitted when the guest changes the RTC time.
 #
@@ -93,7 +93,7 @@
   'data': { 'offset': 'int' } }
 
 ##
-# @WATCHDOG
+# @WATCHDOG:
 #
 # Emitted when the watchdog device's timer is expired
 #
@@ -108,7 +108,7 @@
   'data': { 'action': 'WatchdogExpirationAction' } }
 
 ##
-# @DEVICE_DELETED
+# @DEVICE_DELETED:
 #
 # Emitted whenever the device removal completion is acknowledged by the guest.
 # At this point, it's safe to reuse the specified device ID. Device removal can
@@ -124,7 +124,7 @@
   'data': { '*device': 'str', 'path': 'str' } }
 
 ##
-# @NIC_RX_FILTER_CHANGED
+# @NIC_RX_FILTER_CHANGED:
 #
 # Emitted once until the 'query-rx-filter' command is executed, the first event
 # will always be emitted
@@ -139,7 +139,7 @@
   'data': { '*name': 'str', 'path': 'str' } }
 
 ##
-# @VNC_CONNECTED
+# @VNC_CONNECTED:
 #
 # Emitted when a VNC client establishes a connection
 #
@@ -157,7 +157,7 @@
             'client': 'VncBasicInfo' } }
 
 ##
-# @VNC_INITIALIZED
+# @VNC_INITIALIZED:
 #
 # Emitted after authentication takes place (if any) and the VNC session is
 # made active
@@ -173,7 +173,7 @@
             'client': 'VncClientInfo' } }
 
 ##
-# @VNC_DISCONNECTED
+# @VNC_DISCONNECTED:
 #
 # Emitted when the connection is closed
 #
@@ -188,7 +188,7 @@
             'client': 'VncClientInfo' } }
 
 ##
-# @SPICE_CONNECTED
+# @SPICE_CONNECTED:
 #
 # Emitted when a SPICE client establishes a connection
 #
@@ -203,7 +203,7 @@
             'client': 'SpiceBasicInfo' } }
 
 ##
-# @SPICE_INITIALIZED
+# @SPICE_INITIALIZED:
 #
 # Emitted after initial handshake and authentication takes place (if any)
 # and the SPICE channel is up and running
@@ -219,7 +219,7 @@
             'client': 'SpiceChannel' } }
 
 ##
-# @SPICE_DISCONNECTED
+# @SPICE_DISCONNECTED:
 #
 # Emitted when the SPICE connection is closed
 #
@@ -234,7 +234,7 @@
             'client': 'SpiceBasicInfo' } }
 
 ##
-# @SPICE_MIGRATE_COMPLETED
+# @SPICE_MIGRATE_COMPLETED:
 #
 # Emitted when SPICE migration has completed
 #
@@ -243,7 +243,7 @@
 { 'event': 'SPICE_MIGRATE_COMPLETED' }
 
 ##
-# @MIGRATION
+# @MIGRATION:
 #
 # Emitted when a migration event happens
 #
@@ -255,7 +255,7 @@
   'data': {'status': 'MigrationStatus'}}
 
 ##
-# @MIGRATION_PASS
+# @MIGRATION_PASS:
 #
 # Emitted from the source side of a migration at the start of each pass
 # (when it syncs the dirty bitmap)
@@ -268,7 +268,7 @@
   'data': { 'pass': 'int' } }
 
 ##
-# @ACPI_DEVICE_OST
+# @ACPI_DEVICE_OST:
 #
 # Emitted when guest executes ACPI _OST method.
 #
@@ -280,7 +280,7 @@
      'data': { 'info': 'ACPIOSTInfo' } }
 
 ##
-# @BALLOON_CHANGE
+# @BALLOON_CHANGE:
 #
 # Emitted when the guest changes the actual BALLOON level. This value is
 # equivalent to the @actual field return by the 'query-balloon' command
@@ -293,7 +293,7 @@
   'data': { 'actual': 'int' } }
 
 ##
-# @GUEST_PANICKED
+# @GUEST_PANICKED:
 #
 # Emitted when guest OS panic is detected
 #
@@ -305,7 +305,7 @@
   'data': { 'action': 'GuestPanicAction' } }
 
 ##
-# @QUORUM_FAILURE
+# @QUORUM_FAILURE:
 #
 # Emitted by the Quorum block driver if it fails to establish a quorum
 #
@@ -321,7 +321,7 @@
   'data': { 'reference': 'str', 'sector-num': 'int', 'sectors-count': 'int' } }
 
 ##
-# @QUORUM_REPORT_BAD
+# @QUORUM_REPORT_BAD:
 #
 # Emitted to report a corruption of a Quorum file
 #
@@ -345,7 +345,7 @@
             'sector-num': 'int', 'sectors-count': 'int' } }
 
 ##
-# @VSERPORT_CHANGE
+# @VSERPORT_CHANGE:
 #
 # Emitted when the guest opens or closes a virtio-serial port.
 #
@@ -359,7 +359,7 @@
   'data': { 'id': 'str', 'open': 'bool' } }
 
 ##
-# @MEM_UNPLUG_ERROR
+# @MEM_UNPLUG_ERROR:
 #
 # Emitted when memory hot unplug error occurs.
 #
@@ -373,7 +373,7 @@
   'data': { 'device': 'str', 'msg': 'str' } }
 
 ##
-# @DUMP_COMPLETED
+# @DUMP_COMPLETED:
 #
 # Emitted when background dump has completed
 #
diff --git a/qapi/introspect.json b/qapi/introspect.json
index 3fd81fb..fd4dc84 100644
--- a/qapi/introspect.json
+++ b/qapi/introspect.json
@@ -11,7 +11,7 @@
 # See the COPYING file in the top-level directory.
 
 ##
-# @query-qmp-schema
+# @query-qmp-schema:
 #
 # Command query-qmp-schema exposes the QMP wire ABI as an array of
 # SchemaInfo.  This lets QMP clients figure out what commands and
@@ -49,7 +49,7 @@
   'gen': false }                # just to simplify qmp_query_json()
 
 ##
-# @SchemaMetaType
+# @SchemaMetaType:
 #
 # This is a @SchemaInfo's meta type, i.e. the kind of entity it
 # describes.
@@ -75,7 +75,7 @@
             'command', 'event' ] }
 
 ##
-# @SchemaInfo
+# @SchemaInfo:
 #
 # @name: the entity's name, inherited from @base.
 #        Commands and events have the name defined in the QAPI schema.
@@ -105,7 +105,7 @@
       'event': 'SchemaInfoEvent' } }
 
 ##
-# @SchemaInfoBuiltin
+# @SchemaInfoBuiltin:
 #
 # Additional SchemaInfo members for meta-type 'builtin'.
 #
@@ -117,7 +117,7 @@
   'data': { 'json-type': 'JSONType' } }
 
 ##
-# @JSONType
+# @JSONType:
 #
 # The four primitive and two structured types according to RFC 7159
 # section 1, plus 'int' (split off 'number'), plus the obvious top
@@ -130,7 +130,7 @@
             'object', 'array', 'value' ] }
 
 ##
-# @SchemaInfoEnum
+# @SchemaInfoEnum:
 #
 # Additional SchemaInfo members for meta-type 'enum'.
 #
@@ -144,7 +144,7 @@
   'data': { 'values': ['str'] } }
 
 ##
-# @SchemaInfoArray
+# @SchemaInfoArray:
 #
 # Additional SchemaInfo members for meta-type 'array'.
 #
@@ -158,7 +158,7 @@
   'data': { 'element-type': 'str' } }
 
 ##
-# @SchemaInfoObject
+# @SchemaInfoObject:
 #
 # Additional SchemaInfo members for meta-type 'object'.
 #
@@ -183,7 +183,7 @@
             '*variants': [ 'SchemaInfoObjectVariant' ] } }
 
 ##
-# @SchemaInfoObjectMember
+# @SchemaInfoObjectMember:
 #
 # An object member.
 #
@@ -206,7 +206,7 @@
 # @default's type must be null or match @type
 
 ##
-# @SchemaInfoObjectVariant
+# @SchemaInfoObjectVariant:
 #
 # The variant members for a value of the type tag.
 #
@@ -221,7 +221,7 @@
   'data': { 'case': 'str', 'type': 'str' } }
 
 ##
-# @SchemaInfoAlternate
+# @SchemaInfoAlternate:
 #
 # Additional SchemaInfo members for meta-type 'alternate'.
 #
@@ -237,7 +237,7 @@
   'data': { 'members': [ 'SchemaInfoAlternateMember' ] } }
 
 ##
-# @SchemaInfoAlternateMember
+# @SchemaInfoAlternateMember:
 #
 # An alternate member.
 #
@@ -249,7 +249,7 @@
   'data': { 'type': 'str' } }
 
 ##
-# @SchemaInfoCommand
+# @SchemaInfoCommand:
 #
 # Additional SchemaInfo members for meta-type 'command'.
 #
@@ -266,7 +266,7 @@
   'data': { 'arg-type': 'str', 'ret-type': 'str' } }
 
 ##
-# @SchemaInfoEvent
+# @SchemaInfoEvent:
 #
 # Additional SchemaInfo members for meta-type 'event'.
 #
diff --git a/qapi/trace.json b/qapi/trace.json
index e872146..4fd39b7 100644
--- a/qapi/trace.json
+++ b/qapi/trace.json
@@ -17,7 +17,7 @@
 #
 # @enabled: The event is dynamically enabled.
 #
-# Since 2.2
+# Since: 2.2
 ##
 { 'enum': 'TraceEventState',
   'data': ['unavailable', 'disabled', 'enabled'] }
@@ -34,7 +34,7 @@
 # An event is per-vCPU if it has the "vcpu" property in the "trace-events"
 # files.
 #
-# Since 2.2
+# Since: 2.2
 ##
 { 'struct': 'TraceEventInfo',
   'data': {'name': 'str', 'state': 'TraceEventState', 'vcpu': 'bool'} }
@@ -58,7 +58,7 @@
 # exact match, @vcpu is given and the event does not have the "vcpu" property,
 # an error is returned.
 #
-# Since 2.2
+# Since: 2.2
 ##
 { 'command': 'trace-event-get-state',
   'data': {'name': 'str', '*vcpu': 'int'},
@@ -83,7 +83,7 @@
 # match, @vcpu is given and the event does not have the "vcpu" property, an
 # error is returned.
 #
-# Since 2.2
+# Since: 2.2
 ##
 { 'command': 'trace-event-set-state',
   'data': {'name': 'str', 'enable': 'bool', '*ignore-unavailable': 'bool',
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index 2e6cc91..0633515 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -102,7 +102,7 @@
 #
 # Returns: Time in nanoseconds.
 #
-# Since 1.5
+# Since: 1.5
 ##
 { 'command': 'guest-get-time',
   'returns': 'int' }
@@ -149,13 +149,13 @@
 # @success-response: whether command returns a response on success
 #                    (since 1.7)
 #
-# Since 1.1.0
+# Since: 1.1.0
 ##
 { 'struct': 'GuestAgentCommandInfo',
   'data': { 'name': 'str', 'enabled': 'bool', 'success-response': 'bool' } }
 
 ##
-# @GuestAgentInfo
+# @GuestAgentInfo:
 #
 # Information about guest agent.
 #
@@ -163,7 +163,7 @@
 #
 # @supported_commands: Information about guest agent commands
 #
-# Since 0.15.0
+# Since: 0.15.0
 ##
 { 'struct': 'GuestAgentInfo',
   'data': { 'version': 'str',
@@ -230,7 +230,7 @@
   'data': { 'handle': 'int' } }
 
 ##
-# @GuestFileRead
+# @GuestFileRead:
 #
 # Result of guest agent file-read operation
 #
@@ -264,7 +264,7 @@
   'returns': 'GuestFileRead' }
 
 ##
-# @GuestFileWrite
+# @GuestFileWrite:
 #
 # Result of guest agent file-write operation
 #
@@ -300,7 +300,7 @@
 
 
 ##
-# @GuestFileSeek
+# @GuestFileSeek:
 #
 # Result of guest agent file-seek operation
 #
@@ -378,7 +378,7 @@
   'data': { 'handle': 'int' } }
 
 ##
-# @GuestFsfreezeStatus
+# @GuestFsfreezeStatus:
 #
 # An enumeration of filesystem freeze states
 #
@@ -455,7 +455,7 @@
   'returns': 'int' }
 
 ##
-# @GuestFilesystemTrimResult
+# @GuestFilesystemTrimResult:
 #
 # @path: path that was trimmed
 # @error: an error message when trim failed
@@ -469,7 +469,7 @@
            '*trimmed': 'int', '*minimum': 'int', '*error': 'str'} }
 
 ##
-# @GuestFilesystemTrimResponse
+# @GuestFilesystemTrimResponse:
 #
 # @paths: list of @GuestFilesystemTrimResult per path that was trimmed
 #
@@ -501,7 +501,7 @@
   'returns': 'GuestFilesystemTrimResponse' }
 
 ##
-# @guest-suspend-disk
+# @guest-suspend-disk:
 #
 # Suspend guest to disk.
 #
@@ -529,7 +529,7 @@
 { 'command': 'guest-suspend-disk', 'success-response': false }
 
 ##
-# @guest-suspend-ram
+# @guest-suspend-ram:
 #
 # Suspend guest to ram.
 #
@@ -561,7 +561,7 @@
 { 'command': 'guest-suspend-ram', 'success-response': false }
 
 ##
-# @guest-suspend-hybrid
+# @guest-suspend-hybrid:
 #
 # Save guest state to disk and suspend to ram.
 #
@@ -716,7 +716,7 @@
   'returns': 'int' }
 
 ##
-# @GuestDiskBusType
+# @GuestDiskBusType:
 #
 # An enumeration of bus type of disks
 #
@@ -779,7 +779,7 @@
            'bus': 'int', 'target': 'int', 'unit': 'int'} }
 
 ##
-# @GuestFilesystemInfo
+# @GuestFilesystemInfo:
 #
 # @name: disk name
 # @mountpoint: mount point path
@@ -807,7 +807,7 @@
   'returns': ['GuestFilesystemInfo'] }
 
 ##
-# @guest-set-user-password
+# @guest-set-user-password:
 #
 # @username: the user account whose password to change
 # @password: the new password entry string, base64 encoded
@@ -828,7 +828,7 @@
 #
 # Returns: Nothing on success.
 #
-# Since 2.3
+# Since: 2.3
 ##
 { 'command': 'guest-set-user-password',
   'data': { 'username': 'str', 'password': 'str', 'crypted': 'bool' } }
@@ -869,7 +869,7 @@
   'returns': ['GuestMemoryBlock'] }
 
 ##
-# @GuestMemoryBlockResponseType
+# @GuestMemoryBlockResponseType:
 #
 # An enumeration of memory block operation result.
 #
@@ -952,7 +952,7 @@
 #
 # Returns: @GuestMemoryBlockInfo
 #
-# Since 2.3
+# Since: 2.3
 ##
 { 'command': 'guest-get-memory-block-info',
   'returns': 'GuestMemoryBlockInfo' }
@@ -980,7 +980,7 @@
             '*out-data': 'str', '*err-data': 'str',
             '*out-truncated': 'bool', '*err-truncated': 'bool' }}
 ##
-# @guest-exec-status
+# @guest-exec-status:
 #
 # Check status of process associated with PID retrieved via guest-exec.
 # Reap the process and associated metadata if it has exited.
@@ -989,7 +989,7 @@
 #
 # Returns: GuestExecStatus on success.
 #
-# Since 2.5
+# Since: 2.5
 ##
 { 'command': 'guest-exec-status',
   'data':    { 'pid': 'int' },
@@ -999,7 +999,7 @@
 # @GuestExec:
 # @pid: pid of child process in guest OS
 #
-#Since: 2.5
+# Since: 2.5
 ##
 { 'struct': 'GuestExec',
   'data': { 'pid': 'int'} }
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [Qemu-devel] [PATCH v5 09/17] qapi: add some sections in docs
  2016-11-17 15:54 [Qemu-devel] [PATCH v5 00/17] qapi doc generation (whole version, squashed) Marc-André Lureau
                   ` (7 preceding siblings ...)
  2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 08/17] qapi: add missing colon-ending for section name Marc-André Lureau
@ 2016-11-17 15:54 ` Marc-André Lureau
  2016-11-17 17:43   ` Markus Armbruster
  2016-11-30 15:38   ` Markus Armbruster
  2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 10/17] qapi: improve TransactionAction doc Marc-André Lureau
                   ` (8 subsequent siblings)
  17 siblings, 2 replies; 48+ messages in thread
From: Marc-André Lureau @ 2016-11-17 15:54 UTC (permalink / raw)
  To: qemu-devel; +Cc: eblake, armbru, Marc-André Lureau

Add some more section titles to organize the produced documents.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 qapi-schema.json     |  4 ++++
 qapi/block-core.json |  6 ++++--
 qapi/block.json      | 10 ++++++++--
 qapi/common.json     |  6 ++++--
 qapi/crypto.json     |  5 ++++-
 qapi/event.json      |  6 ++++++
 qapi/rocker.json     |  4 ++++
 qapi/trace.json      |  3 +++
 8 files changed, 37 insertions(+), 7 deletions(-)

diff --git a/qapi-schema.json b/qapi-schema.json
index dfe68ba..69340f2 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -20,6 +20,10 @@
 # QAPI introspection
 { 'include': 'qapi/introspect.json' }
 
+##
+# = QMP commands
+##
+
 ##
 # @qmp_capabilities:
 #
diff --git a/qapi/block-core.json b/qapi/block-core.json
index ec1da2a..05cedc3 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -1,6 +1,8 @@
 # -*- Mode: Python -*-
-#
-# QAPI block core definitions (vm unrelated)
+
+##
+# == QAPI block core definitions (vm unrelated)
+##
 
 # QAPI common definitions
 { 'include': 'common.json' }
diff --git a/qapi/block.json b/qapi/block.json
index 937df05..e4ad74b 100644
--- a/qapi/block.json
+++ b/qapi/block.json
@@ -1,10 +1,16 @@
 # -*- Mode: Python -*-
-#
-# QAPI block definitions (vm related)
+
+##
+# = QAPI block definitions
+##
 
 # QAPI block core definitions
 { 'include': 'block-core.json' }
 
+##
+# == QAPI block definitions (vm unrelated)
+##
+
 ##
 # @BiosAtaTranslation:
 #
diff --git a/qapi/common.json b/qapi/common.json
index 624a861..d93f159 100644
--- a/qapi/common.json
+++ b/qapi/common.json
@@ -1,6 +1,8 @@
 # -*- Mode: Python -*-
-#
-# QAPI common definitions
+
+##
+# = QAPI common definitions
+##
 
 ##
 # @QapiErrorClass:
diff --git a/qapi/crypto.json b/qapi/crypto.json
index 15d296e..1e517b0 100644
--- a/qapi/crypto.json
+++ b/qapi/crypto.json
@@ -1,6 +1,9 @@
 # -*- Mode: Python -*-
 #
-# QAPI crypto definitions
+
+##
+# = QAPI crypto definitions
+##
 
 ##
 # @QCryptoTLSCredsEndpoint:
diff --git a/qapi/event.json b/qapi/event.json
index 37bf34e..59942b0 100644
--- a/qapi/event.json
+++ b/qapi/event.json
@@ -1,3 +1,9 @@
+# -*- Mode: Python -*-
+
+##
+# = Events
+##
+
 ##
 # @SHUTDOWN:
 #
diff --git a/qapi/rocker.json b/qapi/rocker.json
index ace2776..dd72e02 100644
--- a/qapi/rocker.json
+++ b/qapi/rocker.json
@@ -1,4 +1,8 @@
 ##
+# = Rocker API
+##
+
+##
 # @RockerSwitch:
 #
 # Rocker switch information.
diff --git a/qapi/trace.json b/qapi/trace.json
index 4fd39b7..3ad7df7 100644
--- a/qapi/trace.json
+++ b/qapi/trace.json
@@ -5,6 +5,9 @@
 # This work is licensed under the terms of the GNU GPL, version 2 or later.
 # See the COPYING file in the top-level directory.
 
+##
+# = Tracing commands
+##
 
 ##
 # @TraceEventState:
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [Qemu-devel] [PATCH v5 10/17] qapi: improve TransactionAction doc
  2016-11-17 15:54 [Qemu-devel] [PATCH v5 00/17] qapi doc generation (whole version, squashed) Marc-André Lureau
                   ` (8 preceding siblings ...)
  2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 09/17] qapi: add some sections in docs Marc-André Lureau
@ 2016-11-17 15:54 ` Marc-André Lureau
  2016-11-17 18:03   ` Markus Armbruster
  2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 11/17] docs: add master qapi texi files Marc-André Lureau
                   ` (7 subsequent siblings)
  17 siblings, 1 reply; 48+ messages in thread
From: Marc-André Lureau @ 2016-11-17 15:54 UTC (permalink / raw)
  To: qemu-devel; +Cc: eblake, armbru, Marc-André Lureau

TransactionAction is a flat union, document 'type' versions
exhaustively, and sort list.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 qapi-schema.json | 31 ++++++++++++++++---------------
 1 file changed, 16 insertions(+), 15 deletions(-)

diff --git a/qapi-schema.json b/qapi-schema.json
index 69340f2..94f1a2e 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -1797,28 +1797,29 @@
 # @TransactionAction:
 #
 # A discriminated record of operations that can be performed with
-# @transaction.
+# @transaction. Action @type can be:
 #
-# Since: 1.1
+# - @abort: since 1.6
+# - @block-dirty-bitmap-add: since 2.5
+# - @block-dirty-bitmap-clear: since 2.5
+# - @blockdev-backup: since 2.3
+# - @blockdev-snapshot: since 2.5
+# - @blockdev-snapshot-internal-sync: since 1.7
+# - @blockdev-snapshot-sync: since 1.1
+# - @drive-backup: since 1.6
 #
-# drive-backup since 1.6
-# abort since 1.6
-# blockdev-snapshot-internal-sync since 1.7
-# blockdev-backup since 2.3
-# blockdev-snapshot since 2.5
-# block-dirty-bitmap-add since 2.5
-# block-dirty-bitmap-clear since 2.5
+# Since: 1.1
 ##
 { 'union': 'TransactionAction',
   'data': {
-       'blockdev-snapshot': 'BlockdevSnapshot',
-       'blockdev-snapshot-sync': 'BlockdevSnapshotSync',
-       'drive-backup': 'DriveBackup',
-       'blockdev-backup': 'BlockdevBackup',
        'abort': 'Abort',
-       'blockdev-snapshot-internal-sync': 'BlockdevSnapshotInternal',
        'block-dirty-bitmap-add': 'BlockDirtyBitmapAdd',
-       'block-dirty-bitmap-clear': 'BlockDirtyBitmap'
+       'block-dirty-bitmap-clear': 'BlockDirtyBitmap',
+       'blockdev-backup': 'BlockdevBackup',
+       'blockdev-snapshot': 'BlockdevSnapshot',
+       'blockdev-snapshot-internal-sync': 'BlockdevSnapshotInternal',
+       'blockdev-snapshot-sync': 'BlockdevSnapshotSync',
+       'drive-backup': 'DriveBackup'
    } }
 
 ##
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [Qemu-devel] [PATCH v5 11/17] docs: add master qapi texi files
  2016-11-17 15:54 [Qemu-devel] [PATCH v5 00/17] qapi doc generation (whole version, squashed) Marc-André Lureau
                   ` (9 preceding siblings ...)
  2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 10/17] qapi: improve TransactionAction doc Marc-André Lureau
@ 2016-11-17 15:54 ` Marc-André Lureau
  2016-11-18  9:09   ` Markus Armbruster
  2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 12/17] qapi: rename QAPIExprError/QAPILineError Marc-André Lureau
                   ` (6 subsequent siblings)
  17 siblings, 1 reply; 48+ messages in thread
From: Marc-André Lureau @ 2016-11-17 15:54 UTC (permalink / raw)
  To: qemu-devel; +Cc: eblake, armbru, Marc-André Lureau

The qapi2texi script generates a file to be included in a texi file. Add
"QEMU QMP Reference Manual" and "QEMU Guest Agent Protocol Reference"
master texi files.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 docs/qemu-ga-ref.texi  | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++
 docs/qemu-qmp-ref.texi | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 156 insertions(+)
 create mode 100644 docs/qemu-ga-ref.texi
 create mode 100644 docs/qemu-qmp-ref.texi

diff --git a/docs/qemu-ga-ref.texi b/docs/qemu-ga-ref.texi
new file mode 100644
index 0000000..02ecdb7
--- /dev/null
+++ b/docs/qemu-ga-ref.texi
@@ -0,0 +1,78 @@
+\input texinfo
+@setfilename qemu-ga-ref.info
+
+@exampleindent 0
+@paragraphindent 0
+
+@settitle QEMU Guest Agent Protocol Reference
+
+@copying
+This is the QEMU Guest Agent Protocol reference manual.
+
+Copyright @copyright{} 2016 The QEMU Project developers
+
+@quotation
+This manual is free documentation: you can redistribute it and/or
+modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation, either version 2 of the
+License, or (at your option) any later version.
+
+This manual is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this manual.  If not, see http://www.gnu.org/licenses/.
+@end quotation
+@end copying
+
+@dircategory QEMU
+@direntry
+* QEMU-GA-Ref: (qemu-ga-ref).   QEMU Guest Agent Protocol Reference
+@end direntry
+
+@titlepage
+@title Guest Agent Protocol Reference Manual
+@subtitle QEMU version @value{VERSION}
+@page
+@vskip 0pt plus 1filll
+@insertcopying
+@end titlepage
+
+@contents
+
+@ifnottex
+@node Top
+@top QEMU Guest Agent protocol reference
+@end ifnottex
+
+@menu
+* API Reference::
+* Commands and Events Index::
+* Data Types Index::
+@end menu
+
+@node API Reference
+@chapter API Reference
+
+@c for texi2pod:
+@c man begin DESCRIPTION
+
+@include qemu-ga-qapi.texi
+
+@c man end
+
+@c man begin SEEALSO
+The HTML documentation of QEMU for more information.
+@c man end
+
+@node Commands and Events Index
+@unnumbered Commands and Events Index
+@printindex fn
+
+@node Data Types Index
+@unnumbered Data Types Index
+@printindex tp
+
+@bye
diff --git a/docs/qemu-qmp-ref.texi b/docs/qemu-qmp-ref.texi
new file mode 100644
index 0000000..ccc03cb
--- /dev/null
+++ b/docs/qemu-qmp-ref.texi
@@ -0,0 +1,78 @@
+\input texinfo
+@setfilename qemu-qmp-ref.info
+
+@exampleindent 0
+@paragraphindent 0
+
+@settitle QEMU QMP Reference Manual
+
+@copying
+This is the QEMU QMP reference manual.
+
+Copyright @copyright{} 2016 The QEMU Project developers
+
+@quotation
+This manual is free documentation: you can redistribute it and/or
+modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation, either version 2 of the
+License, or (at your option) any later version.
+
+This manual is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this manual.  If not, see http://www.gnu.org/licenses/.
+@end quotation
+@end copying
+
+@dircategory QEMU
+@direntry
+* QEMU-QMP-Ref: (qemu-qmp-ref). QEMU QMP Reference Manual
+@end direntry
+
+@titlepage
+@title QMP Reference Manual
+@subtitle QEMU version @value{VERSION}
+@page
+@vskip 0pt plus 1filll
+@insertcopying
+@end titlepage
+
+@contents
+
+@ifnottex
+@node Top
+@top QEMU QMP reference
+@end ifnottex
+
+@menu
+* API Reference::
+* Commands and Events Index::
+* Data Types Index::
+@end menu
+
+@node API Reference
+@chapter API Reference
+
+@c for texi2pod:
+@c man begin DESCRIPTION
+
+@include qemu-qapi.texi
+
+@c man end
+
+@c man begin SEEALSO
+The HTML documentation of QEMU for more information.
+@c man end
+
+@node Commands and Events Index
+@unnumbered Commands and Events Index
+@printindex fn
+
+@node Data Types Index
+@unnumbered Data Types Index
+@printindex tp
+
+@bye
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [Qemu-devel] [PATCH v5 12/17] qapi: rename QAPIExprError/QAPILineError
  2016-11-17 15:54 [Qemu-devel] [PATCH v5 00/17] qapi doc generation (whole version, squashed) Marc-André Lureau
                   ` (10 preceding siblings ...)
  2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 11/17] docs: add master qapi texi files Marc-André Lureau
@ 2016-11-17 15:54 ` Marc-André Lureau
  2016-11-18 10:17   ` Markus Armbruster
  2016-11-17 15:55 ` [Qemu-devel] [PATCH v5 13/17] qapi: add qapi2texi script Marc-André Lureau
                   ` (5 subsequent siblings)
  17 siblings, 1 reply; 48+ messages in thread
From: Marc-André Lureau @ 2016-11-17 15:54 UTC (permalink / raw)
  To: qemu-devel; +Cc: eblake, armbru, Marc-André Lureau

There is nothing specific about expressions in this exception,
the following patch will use it without expressions.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 scripts/qapi.py | 146 ++++++++++++++++++++++++++++----------------------------
 1 file changed, 73 insertions(+), 73 deletions(-)

diff --git a/scripts/qapi.py b/scripts/qapi.py
index 21bc32f..4d1b0e4 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -110,11 +110,11 @@ class QAPISchemaError(Exception):
             "%s:%d:%d: %s" % (self.fname, self.line, self.col, self.msg)
 
 
-class QAPIExprError(Exception):
-    def __init__(self, expr_info, msg):
+class QAPILineError(Exception):
+    def __init__(self, info, msg):
         Exception.__init__(self)
-        assert expr_info
-        self.info = expr_info
+        assert info
+        self.info = info
         self.msg = msg
 
     def __str__(self):
@@ -140,24 +140,24 @@ class QAPISchemaParser(object):
         self.accept()
 
         while self.tok is not None:
-            expr_info = {'file': fname, 'line': self.line,
-                         'parent': self.incl_info}
+            info = {'file': fname, 'line': self.line,
+                    'parent': self.incl_info}
             expr = self.get_expr(False)
             if isinstance(expr, dict) and "include" in expr:
                 if len(expr) != 1:
-                    raise QAPIExprError(expr_info,
+                    raise QAPILineError(info,
                                         "Invalid 'include' directive")
                 include = expr["include"]
                 if not isinstance(include, str):
-                    raise QAPIExprError(expr_info,
+                    raise QAPILineError(info,
                                         "Value of 'include' must be a string")
                 incl_abs_fname = os.path.join(os.path.dirname(abs_fname),
                                               include)
                 # catch inclusion cycle
-                inf = expr_info
+                inf = info
                 while inf:
                     if incl_abs_fname == os.path.abspath(inf['file']):
-                        raise QAPIExprError(expr_info, "Inclusion loop for %s"
+                        raise QAPILineError(info, "Inclusion loop for %s"
                                             % include)
                     inf = inf['parent']
                 # skip multiple include of the same file
@@ -166,14 +166,14 @@ class QAPISchemaParser(object):
                 try:
                     fobj = open(incl_abs_fname, 'r')
                 except IOError as e:
-                    raise QAPIExprError(expr_info,
+                    raise QAPILineError(info,
                                         '%s: %s' % (e.strerror, include))
                 exprs_include = QAPISchemaParser(fobj, previously_included,
-                                                 expr_info)
+                                                 info)
                 self.exprs.extend(exprs_include.exprs)
             else:
                 expr_elem = {'expr': expr,
-                             'info': expr_info}
+                             'info': info}
                 self.exprs.append(expr_elem)
 
     def accept(self):
@@ -375,18 +375,18 @@ valid_name = re.compile('^(__[a-zA-Z0-9.-]+_)?'
                         '[a-zA-Z][a-zA-Z0-9_-]*$')
 
 
-def check_name(expr_info, source, name, allow_optional=False,
+def check_name(info, source, name, allow_optional=False,
                enum_member=False):
     global valid_name
     membername = name
 
     if not isinstance(name, str):
-        raise QAPIExprError(expr_info,
+        raise QAPILineError(info,
                             "%s requires a string name" % source)
     if name.startswith('*'):
         membername = name[1:]
         if not allow_optional:
-            raise QAPIExprError(expr_info,
+            raise QAPILineError(info,
                                 "%s does not allow optional name '%s'"
                                 % (source, name))
     # Enum members can start with a digit, because the generated C
@@ -397,7 +397,7 @@ def check_name(expr_info, source, name, allow_optional=False,
     # and 'q_obj_*' implicit type names.
     if not valid_name.match(membername) or \
        c_name(membername, False).startswith('q_'):
-        raise QAPIExprError(expr_info,
+        raise QAPILineError(info,
                             "%s uses invalid name '%s'" % (source, name))
 
 
@@ -407,11 +407,11 @@ def add_name(name, info, meta, implicit=False):
     # FIXME should reject names that differ only in '_' vs. '.'
     # vs. '-', because they're liable to clash in generated C.
     if name in all_names:
-        raise QAPIExprError(info,
+        raise QAPILineError(info,
                             "%s '%s' is already defined"
                             % (all_names[name], name))
     if not implicit and (name.endswith('Kind') or name.endswith('List')):
-        raise QAPIExprError(info,
+        raise QAPILineError(info,
                             "%s '%s' should not end in '%s'"
                             % (meta, name, name[-4:]))
     all_names[name] = meta
@@ -465,7 +465,7 @@ def is_enum(name):
     return find_enum(name) is not None
 
 
-def check_type(expr_info, source, value, allow_array=False,
+def check_type(info, source, value, allow_array=False,
                allow_dict=False, allow_optional=False,
                allow_metas=[]):
     global all_names
@@ -476,10 +476,10 @@ def check_type(expr_info, source, value, allow_array=False,
     # Check if array type for value is okay
     if isinstance(value, list):
         if not allow_array:
-            raise QAPIExprError(expr_info,
+            raise QAPILineError(info,
                                 "%s cannot be an array" % source)
         if len(value) != 1 or not isinstance(value[0], str):
-            raise QAPIExprError(expr_info,
+            raise QAPILineError(info,
                                 "%s: array type must contain single type name"
                                 % source)
         value = value[0]
@@ -487,58 +487,58 @@ def check_type(expr_info, source, value, allow_array=False,
     # Check if type name for value is okay
     if isinstance(value, str):
         if value not in all_names:
-            raise QAPIExprError(expr_info,
+            raise QAPILineError(info,
                                 "%s uses unknown type '%s'"
                                 % (source, value))
         if not all_names[value] in allow_metas:
-            raise QAPIExprError(expr_info,
+            raise QAPILineError(info,
                                 "%s cannot use %s type '%s'"
                                 % (source, all_names[value], value))
         return
 
     if not allow_dict:
-        raise QAPIExprError(expr_info,
+        raise QAPILineError(info,
                             "%s should be a type name" % source)
 
     if not isinstance(value, OrderedDict):
-        raise QAPIExprError(expr_info,
+        raise QAPILineError(info,
                             "%s should be a dictionary or type name" % source)
 
     # value is a dictionary, check that each member is okay
     for (key, arg) in value.items():
-        check_name(expr_info, "Member of %s" % source, key,
+        check_name(info, "Member of %s" % source, key,
                    allow_optional=allow_optional)
         if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
-            raise QAPIExprError(expr_info,
+            raise QAPILineError(info,
                                 "Member of %s uses reserved name '%s'"
                                 % (source, key))
         # Todo: allow dictionaries to represent default values of
         # an optional argument.
-        check_type(expr_info, "Member '%s' of %s" % (key, source), arg,
+        check_type(info, "Member '%s' of %s" % (key, source), arg,
                    allow_array=True,
                    allow_metas=['built-in', 'union', 'alternate', 'struct',
                                 'enum'])
 
 
-def check_command(expr, expr_info):
+def check_command(expr, info):
     name = expr['command']
     boxed = expr.get('boxed', False)
 
     args_meta = ['struct']
     if boxed:
         args_meta += ['union', 'alternate']
-    check_type(expr_info, "'data' for command '%s'" % name,
+    check_type(info, "'data' for command '%s'" % name,
                expr.get('data'), allow_dict=not boxed, allow_optional=True,
                allow_metas=args_meta)
     returns_meta = ['union', 'struct']
     if name in returns_whitelist:
         returns_meta += ['built-in', 'alternate', 'enum']
-    check_type(expr_info, "'returns' for command '%s'" % name,
+    check_type(info, "'returns' for command '%s'" % name,
                expr.get('returns'), allow_array=True,
                allow_optional=True, allow_metas=returns_meta)
 
 
-def check_event(expr, expr_info):
+def check_event(expr, info):
     global events
     name = expr['event']
     boxed = expr.get('boxed', False)
@@ -547,12 +547,12 @@ def check_event(expr, expr_info):
     if boxed:
         meta += ['union', 'alternate']
     events.append(name)
-    check_type(expr_info, "'data' for event '%s'" % name,
+    check_type(info, "'data' for event '%s'" % name,
                expr.get('data'), allow_dict=not boxed, allow_optional=True,
                allow_metas=meta)
 
 
-def check_union(expr, expr_info):
+def check_union(expr, info):
     name = expr['union']
     base = expr.get('base')
     discriminator = expr.get('discriminator')
@@ -565,18 +565,18 @@ def check_union(expr, expr_info):
         enum_define = None
         allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
         if base is not None:
-            raise QAPIExprError(expr_info,
+            raise QAPILineError(info,
                                 "Simple union '%s' must not have a base"
                                 % name)
 
     # Else, it's a flat union.
     else:
         # The object must have a string or dictionary 'base'.
-        check_type(expr_info, "'base' for union '%s'" % name,
+        check_type(info, "'base' for union '%s'" % name,
                    base, allow_dict=True, allow_optional=True,
                    allow_metas=['struct'])
         if not base:
-            raise QAPIExprError(expr_info,
+            raise QAPILineError(info,
                                 "Flat union '%s' must have a base"
                                 % name)
         base_members = find_base_members(base)
@@ -584,11 +584,11 @@ def check_union(expr, expr_info):
 
         # The value of member 'discriminator' must name a non-optional
         # member of the base struct.
-        check_name(expr_info, "Discriminator of flat union '%s'" % name,
+        check_name(info, "Discriminator of flat union '%s'" % name,
                    discriminator)
         discriminator_type = base_members.get(discriminator)
         if not discriminator_type:
-            raise QAPIExprError(expr_info,
+            raise QAPILineError(info,
                                 "Discriminator '%s' is not a member of base "
                                 "struct '%s'"
                                 % (discriminator, base))
@@ -596,26 +596,26 @@ def check_union(expr, expr_info):
         allow_metas = ['struct']
         # Do not allow string discriminator
         if not enum_define:
-            raise QAPIExprError(expr_info,
+            raise QAPILineError(info,
                                 "Discriminator '%s' must be of enumeration "
                                 "type" % discriminator)
 
     # Check every branch; don't allow an empty union
     if len(members) == 0:
-        raise QAPIExprError(expr_info,
+        raise QAPILineError(info,
                             "Union '%s' cannot have empty 'data'" % name)
     for (key, value) in members.items():
-        check_name(expr_info, "Member of union '%s'" % name, key)
+        check_name(info, "Member of union '%s'" % name, key)
 
         # Each value must name a known type
-        check_type(expr_info, "Member '%s' of union '%s'" % (key, name),
+        check_type(info, "Member '%s' of union '%s'" % (key, name),
                    value, allow_array=not base, allow_metas=allow_metas)
 
         # If the discriminator names an enum type, then all members
         # of 'data' must also be members of the enum type.
         if enum_define:
             if key not in enum_define['enum_values']:
-                raise QAPIExprError(expr_info,
+                raise QAPILineError(info,
                                     "Discriminator value '%s' is not found in "
                                     "enum '%s'" %
                                     (key, enum_define["enum_name"]))
@@ -624,64 +624,64 @@ def check_union(expr, expr_info):
     if enum_define:
         for value in enum_define['enum_values']:
             if value not in members.keys():
-                raise QAPIExprError(expr_info,
+                raise QAPILineError(info,
                                     "Union '%s' data missing '%s' branch"
                                     % (name, value))
 
 
-def check_alternate(expr, expr_info):
+def check_alternate(expr, info):
     name = expr['alternate']
     members = expr['data']
     types_seen = {}
 
     # Check every branch; require at least two branches
     if len(members) < 2:
-        raise QAPIExprError(expr_info,
+        raise QAPILineError(info,
                             "Alternate '%s' should have at least two branches "
                             "in 'data'" % name)
     for (key, value) in members.items():
-        check_name(expr_info, "Member of alternate '%s'" % name, key)
+        check_name(info, "Member of alternate '%s'" % name, key)
 
         # Ensure alternates have no type conflicts.
-        check_type(expr_info, "Member '%s' of alternate '%s'" % (key, name),
+        check_type(info, "Member '%s' of alternate '%s'" % (key, name),
                    value,
                    allow_metas=['built-in', 'union', 'struct', 'enum'])
         qtype = find_alternate_member_qtype(value)
         if not qtype:
-            raise QAPIExprError(expr_info,
+            raise QAPILineError(info,
                                 "Alternate '%s' member '%s' cannot use "
                                 "type '%s'" % (name, key, value))
         if qtype in types_seen:
-            raise QAPIExprError(expr_info,
+            raise QAPILineError(info,
                                 "Alternate '%s' member '%s' can't "
                                 "be distinguished from member '%s'"
                                 % (name, key, types_seen[qtype]))
         types_seen[qtype] = key
 
 
-def check_enum(expr, expr_info):
+def check_enum(expr, info):
     name = expr['enum']
     members = expr.get('data')
     prefix = expr.get('prefix')
 
     if not isinstance(members, list):
-        raise QAPIExprError(expr_info,
+        raise QAPILineError(info,
                             "Enum '%s' requires an array for 'data'" % name)
     if prefix is not None and not isinstance(prefix, str):
-        raise QAPIExprError(expr_info,
+        raise QAPILineError(info,
                             "Enum '%s' requires a string for 'prefix'" % name)
     for member in members:
-        check_name(expr_info, "Member of enum '%s'" % name, member,
+        check_name(info, "Member of enum '%s'" % name, member,
                    enum_member=True)
 
 
-def check_struct(expr, expr_info):
+def check_struct(expr, info):
     name = expr['struct']
     members = expr['data']
 
-    check_type(expr_info, "'data' for struct '%s'" % name, members,
+    check_type(info, "'data' for struct '%s'" % name, members,
                allow_dict=True, allow_optional=True)
-    check_type(expr_info, "'base' for struct '%s'" % name, expr.get('base'),
+    check_type(info, "'base' for struct '%s'" % name, expr.get('base'),
                allow_metas=['struct'])
 
 
@@ -690,25 +690,25 @@ def check_keys(expr_elem, meta, required, optional=[]):
     info = expr_elem['info']
     name = expr[meta]
     if not isinstance(name, str):
-        raise QAPIExprError(info,
+        raise QAPILineError(info,
                             "'%s' key must have a string value" % meta)
     required = required + [meta]
     for (key, value) in expr.items():
         if key not in required and key not in optional:
-            raise QAPIExprError(info,
+            raise QAPILineError(info,
                                 "Unknown key '%s' in %s '%s'"
                                 % (key, meta, name))
         if (key == 'gen' or key == 'success-response') and value is not False:
-            raise QAPIExprError(info,
+            raise QAPILineError(info,
                                 "'%s' of %s '%s' should only use false value"
                                 % (key, meta, name))
         if key == 'boxed' and value is not True:
-            raise QAPIExprError(info,
+            raise QAPILineError(info,
                                 "'%s' of %s '%s' should only use true value"
                                 % (key, meta, name))
     for key in required:
         if key not in expr:
-            raise QAPIExprError(info,
+            raise QAPILineError(info,
                                 "Key '%s' is missing from %s '%s'"
                                 % (key, meta, name))
 
@@ -743,7 +743,7 @@ def check_exprs(exprs):
             check_keys(expr_elem, 'event', [], ['data', 'boxed'])
             add_name(expr['event'], info, 'event')
         else:
-            raise QAPIExprError(expr_elem['info'],
+            raise QAPILineError(expr_elem['info'],
                                 "Expression is missing metatype")
 
     # Try again for hidden UnionKind enum
@@ -978,7 +978,7 @@ class QAPISchemaObjectType(QAPISchemaType):
 
     def check(self, schema):
         if self.members is False:               # check for cycles
-            raise QAPIExprError(self.info,
+            raise QAPILineError(self.info,
                                 "Object %s contains itself" % self.name)
         if self.members:
             return
@@ -1051,10 +1051,10 @@ class QAPISchemaMember(object):
     def check_clash(self, info, seen):
         cname = c_name(self.name)
         if cname.lower() != cname and self.owner not in case_whitelist:
-            raise QAPIExprError(info,
+            raise QAPILineError(info,
                                 "%s should not use uppercase" % self.describe())
         if cname in seen:
-            raise QAPIExprError(info,
+            raise QAPILineError(info,
                                 "%s collides with %s"
                                 % (self.describe(), seen[cname].describe()))
         seen[cname] = self
@@ -1201,13 +1201,13 @@ class QAPISchemaCommand(QAPISchemaEntity):
             self.arg_type.check(schema)
             if self.boxed:
                 if self.arg_type.is_empty():
-                    raise QAPIExprError(self.info,
+                    raise QAPILineError(self.info,
                                         "Cannot use 'boxed' with empty type")
             else:
                 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
                 assert not self.arg_type.variants
         elif self.boxed:
-            raise QAPIExprError(self.info,
+            raise QAPILineError(self.info,
                                 "Use of 'boxed' requires 'data'")
         if self._ret_type_name:
             self.ret_type = schema.lookup_type(self._ret_type_name)
@@ -1235,13 +1235,13 @@ class QAPISchemaEvent(QAPISchemaEntity):
             self.arg_type.check(schema)
             if self.boxed:
                 if self.arg_type.is_empty():
-                    raise QAPIExprError(self.info,
+                    raise QAPILineError(self.info,
                                         "Cannot use 'boxed' with empty type")
             else:
                 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
                 assert not self.arg_type.variants
         elif self.boxed:
-            raise QAPIExprError(self.info,
+            raise QAPILineError(self.info,
                                 "Use of 'boxed' requires 'data'")
 
     def visit(self, visitor):
@@ -1258,7 +1258,7 @@ class QAPISchema(object):
             self._predefining = False
             self._def_exprs()
             self.check()
-        except (QAPISchemaError, QAPIExprError) as err:
+        except (QAPISchemaError, QAPILineError) as err:
             print >>sys.stderr, err
             exit(1)
 
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [Qemu-devel] [PATCH v5 13/17] qapi: add qapi2texi script
  2016-11-17 15:54 [Qemu-devel] [PATCH v5 00/17] qapi doc generation (whole version, squashed) Marc-André Lureau
                   ` (11 preceding siblings ...)
  2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 12/17] qapi: rename QAPIExprError/QAPILineError Marc-André Lureau
@ 2016-11-17 15:55 ` Marc-André Lureau
  2016-11-30 16:06   ` Markus Armbruster
  2016-11-17 15:55 ` [Qemu-devel] [PATCH v5 14/17] texi2pod: learn quotation, deftp and deftypefn Marc-André Lureau
                   ` (4 subsequent siblings)
  17 siblings, 1 reply; 48+ messages in thread
From: Marc-André Lureau @ 2016-11-17 15:55 UTC (permalink / raw)
  To: qemu-devel; +Cc: eblake, armbru, Marc-André Lureau

As the name suggests, the qapi2texi script converts JSON QAPI
description into a texi file suitable for different target
formats (info/man/txt/pdf/html...).

It parses the following kind of blocks:

Free-form:

  ##
  # = Section
  # == Subsection
  #
  # Some text foo with *emphasis*
  # 1. with a list
  # 2. like that
  #
  # And some code:
  # | $ echo foo
  # | -> do this
  # | <- get that
  #
  ##

Symbol:

  ##
  # @symbol:
  #
  # Symbol body ditto ergo sum. Foo bar
  # baz ding.
  #
  # @arg: foo
  # @arg: #optional foo
  #
  # Returns: returns bla bla
  #          Or bla blah
  #
  # Since: version
  # Notes: notes, comments can have
  #        - itemized list
  #        - like this
  #
  # Example:
  #
  # -> { "execute": "quit" }
  # <- { "return": {} }
  #
  ##

That's roughly following the following EBNF grammar:

api_comment = "##\n" comment "##\n"
comment = freeform_comment | symbol_comment
freeform_comment = { "# " text "\n" | "#\n" }
symbol_comment = "# @" name ":\n" { member | meta | freeform_comment }
member = "# @" name ':' [ text ] freeform_comment
meta = "# " ( "Returns:", "Since:", "Note:", "Notes:", "Example:", "Examples:" ) [ text ] freeform_comment
text = free-text markdown-like, "#optional" for members

Thanks to the following json expressions, the documentation is enhanced
with extra information about the type of arguments and return value
expected.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 tests/Makefile.include                       |  17 ++
 scripts/qapi.py                              | 215 ++++++++++++++++-
 scripts/qapi2texi.py                         | 331 +++++++++++++++++++++++++++
 docs/qapi-code-gen.txt                       |  54 ++++-
 tests/qapi-schema/doc-bad-args.err           |   1 +
 tests/qapi-schema/doc-bad-args.exit          |   1 +
 tests/qapi-schema/doc-bad-args.json          |   6 +
 tests/qapi-schema/doc-bad-args.out           |   0
 tests/qapi-schema/doc-bad-symbol.err         |   1 +
 tests/qapi-schema/doc-bad-symbol.exit        |   1 +
 tests/qapi-schema/doc-bad-symbol.json        |   4 +
 tests/qapi-schema/doc-bad-symbol.out         |   0
 tests/qapi-schema/doc-duplicated-arg.err     |   1 +
 tests/qapi-schema/doc-duplicated-arg.exit    |   1 +
 tests/qapi-schema/doc-duplicated-arg.json    |   5 +
 tests/qapi-schema/doc-duplicated-arg.out     |   0
 tests/qapi-schema/doc-duplicated-return.err  |   1 +
 tests/qapi-schema/doc-duplicated-return.exit |   1 +
 tests/qapi-schema/doc-duplicated-return.json |   6 +
 tests/qapi-schema/doc-duplicated-return.out  |   0
 tests/qapi-schema/doc-duplicated-since.err   |   1 +
 tests/qapi-schema/doc-duplicated-since.exit  |   1 +
 tests/qapi-schema/doc-duplicated-since.json  |   6 +
 tests/qapi-schema/doc-duplicated-since.out   |   0
 tests/qapi-schema/doc-empty-arg.err          |   1 +
 tests/qapi-schema/doc-empty-arg.exit         |   1 +
 tests/qapi-schema/doc-empty-arg.json         |   4 +
 tests/qapi-schema/doc-empty-arg.out          |   0
 tests/qapi-schema/doc-empty-section.err      |   1 +
 tests/qapi-schema/doc-empty-section.exit     |   1 +
 tests/qapi-schema/doc-empty-section.json     |   6 +
 tests/qapi-schema/doc-empty-section.out      |   0
 tests/qapi-schema/doc-empty-symbol.err       |   1 +
 tests/qapi-schema/doc-empty-symbol.exit      |   1 +
 tests/qapi-schema/doc-empty-symbol.json      |   3 +
 tests/qapi-schema/doc-empty-symbol.out       |   0
 tests/qapi-schema/doc-invalid-end.err        |   1 +
 tests/qapi-schema/doc-invalid-end.exit       |   1 +
 tests/qapi-schema/doc-invalid-end.json       |   3 +
 tests/qapi-schema/doc-invalid-end.out        |   0
 tests/qapi-schema/doc-invalid-end2.err       |   1 +
 tests/qapi-schema/doc-invalid-end2.exit      |   1 +
 tests/qapi-schema/doc-invalid-end2.json      |   3 +
 tests/qapi-schema/doc-invalid-end2.out       |   0
 tests/qapi-schema/doc-invalid-return.err     |   1 +
 tests/qapi-schema/doc-invalid-return.exit    |   1 +
 tests/qapi-schema/doc-invalid-return.json    |   5 +
 tests/qapi-schema/doc-invalid-return.out     |   0
 tests/qapi-schema/doc-invalid-section.err    |   1 +
 tests/qapi-schema/doc-invalid-section.exit   |   1 +
 tests/qapi-schema/doc-invalid-section.json   |   4 +
 tests/qapi-schema/doc-invalid-section.out    |   0
 tests/qapi-schema/doc-invalid-start.err      |   1 +
 tests/qapi-schema/doc-invalid-start.exit     |   1 +
 tests/qapi-schema/doc-invalid-start.json     |   3 +
 tests/qapi-schema/doc-invalid-start.out      |   0
 tests/qapi-schema/doc-missing-expr.err       |   1 +
 tests/qapi-schema/doc-missing-expr.exit      |   1 +
 tests/qapi-schema/doc-missing-expr.json      |   3 +
 tests/qapi-schema/doc-missing-expr.out       |   0
 tests/qapi-schema/doc-missing-space.err      |   1 +
 tests/qapi-schema/doc-missing-space.exit     |   1 +
 tests/qapi-schema/doc-missing-space.json     |   4 +
 tests/qapi-schema/doc-missing-space.out      |   0
 tests/qapi-schema/qapi-schema-test.json      |  58 +++++
 tests/qapi-schema/qapi-schema-test.out       |  49 ++++
 tests/qapi-schema/test-qapi.py               |  12 +
 67 files changed, 817 insertions(+), 14 deletions(-)
 create mode 100755 scripts/qapi2texi.py
 create mode 100644 tests/qapi-schema/doc-bad-args.err
 create mode 100644 tests/qapi-schema/doc-bad-args.exit
 create mode 100644 tests/qapi-schema/doc-bad-args.json
 create mode 100644 tests/qapi-schema/doc-bad-args.out
 create mode 100644 tests/qapi-schema/doc-bad-symbol.err
 create mode 100644 tests/qapi-schema/doc-bad-symbol.exit
 create mode 100644 tests/qapi-schema/doc-bad-symbol.json
 create mode 100644 tests/qapi-schema/doc-bad-symbol.out
 create mode 100644 tests/qapi-schema/doc-duplicated-arg.err
 create mode 100644 tests/qapi-schema/doc-duplicated-arg.exit
 create mode 100644 tests/qapi-schema/doc-duplicated-arg.json
 create mode 100644 tests/qapi-schema/doc-duplicated-arg.out
 create mode 100644 tests/qapi-schema/doc-duplicated-return.err
 create mode 100644 tests/qapi-schema/doc-duplicated-return.exit
 create mode 100644 tests/qapi-schema/doc-duplicated-return.json
 create mode 100644 tests/qapi-schema/doc-duplicated-return.out
 create mode 100644 tests/qapi-schema/doc-duplicated-since.err
 create mode 100644 tests/qapi-schema/doc-duplicated-since.exit
 create mode 100644 tests/qapi-schema/doc-duplicated-since.json
 create mode 100644 tests/qapi-schema/doc-duplicated-since.out
 create mode 100644 tests/qapi-schema/doc-empty-arg.err
 create mode 100644 tests/qapi-schema/doc-empty-arg.exit
 create mode 100644 tests/qapi-schema/doc-empty-arg.json
 create mode 100644 tests/qapi-schema/doc-empty-arg.out
 create mode 100644 tests/qapi-schema/doc-empty-section.err
 create mode 100644 tests/qapi-schema/doc-empty-section.exit
 create mode 100644 tests/qapi-schema/doc-empty-section.json
 create mode 100644 tests/qapi-schema/doc-empty-section.out
 create mode 100644 tests/qapi-schema/doc-empty-symbol.err
 create mode 100644 tests/qapi-schema/doc-empty-symbol.exit
 create mode 100644 tests/qapi-schema/doc-empty-symbol.json
 create mode 100644 tests/qapi-schema/doc-empty-symbol.out
 create mode 100644 tests/qapi-schema/doc-invalid-end.err
 create mode 100644 tests/qapi-schema/doc-invalid-end.exit
 create mode 100644 tests/qapi-schema/doc-invalid-end.json
 create mode 100644 tests/qapi-schema/doc-invalid-end.out
 create mode 100644 tests/qapi-schema/doc-invalid-end2.err
 create mode 100644 tests/qapi-schema/doc-invalid-end2.exit
 create mode 100644 tests/qapi-schema/doc-invalid-end2.json
 create mode 100644 tests/qapi-schema/doc-invalid-end2.out
 create mode 100644 tests/qapi-schema/doc-invalid-return.err
 create mode 100644 tests/qapi-schema/doc-invalid-return.exit
 create mode 100644 tests/qapi-schema/doc-invalid-return.json
 create mode 100644 tests/qapi-schema/doc-invalid-return.out
 create mode 100644 tests/qapi-schema/doc-invalid-section.err
 create mode 100644 tests/qapi-schema/doc-invalid-section.exit
 create mode 100644 tests/qapi-schema/doc-invalid-section.json
 create mode 100644 tests/qapi-schema/doc-invalid-section.out
 create mode 100644 tests/qapi-schema/doc-invalid-start.err
 create mode 100644 tests/qapi-schema/doc-invalid-start.exit
 create mode 100644 tests/qapi-schema/doc-invalid-start.json
 create mode 100644 tests/qapi-schema/doc-invalid-start.out
 create mode 100644 tests/qapi-schema/doc-missing-expr.err
 create mode 100644 tests/qapi-schema/doc-missing-expr.exit
 create mode 100644 tests/qapi-schema/doc-missing-expr.json
 create mode 100644 tests/qapi-schema/doc-missing-expr.out
 create mode 100644 tests/qapi-schema/doc-missing-space.err
 create mode 100644 tests/qapi-schema/doc-missing-space.exit
 create mode 100644 tests/qapi-schema/doc-missing-space.json
 create mode 100644 tests/qapi-schema/doc-missing-space.out

diff --git a/tests/Makefile.include b/tests/Makefile.include
index e98d3b6..f16764c 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -350,6 +350,21 @@ qapi-schema += base-cycle-direct.json
 qapi-schema += base-cycle-indirect.json
 qapi-schema += command-int.json
 qapi-schema += comments.json
+qapi-schema += doc-bad-args.json
+qapi-schema += doc-bad-symbol.json
+qapi-schema += doc-duplicated-arg.json
+qapi-schema += doc-duplicated-return.json
+qapi-schema += doc-duplicated-since.json
+qapi-schema += doc-empty-arg.json
+qapi-schema += doc-empty-section.json
+qapi-schema += doc-empty-symbol.json
+qapi-schema += doc-invalid-end.json
+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-expr.json
+qapi-schema += doc-missing-space.json
 qapi-schema += double-data.json
 qapi-schema += double-type.json
 qapi-schema += duplicate-key.json
@@ -443,6 +458,8 @@ qapi-schema += union-optional-branch.json
 qapi-schema += union-unknown.json
 qapi-schema += unknown-escape.json
 qapi-schema += unknown-expr-key.json
+
+
 check-qapi-schema-y := $(addprefix tests/qapi-schema/, $(qapi-schema))
 
 GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h \
diff --git a/scripts/qapi.py b/scripts/qapi.py
index 4d1b0e4..1b456b4 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -122,6 +122,109 @@ class QAPILineError(Exception):
             "%s:%d: %s" % (self.info['file'], self.info['line'], self.msg)
 
 
+class QAPIDoc(object):
+    class Section(object):
+        def __init__(self, name=""):
+            # optional section name (argument/member or section name)
+            self.name = name
+            # the list of strings for this section
+            self.content = []
+
+        def append(self, line):
+            self.content.append(line)
+
+        def __repr__(self):
+            return "\n".join(self.content).strip()
+
+    class ArgSection(Section):
+        pass
+
+    def __init__(self, parser):
+        self.parser = parser
+        self.symbol = None
+        self.body = QAPIDoc.Section()
+        # a dict {'arg': ArgSection, ...}
+        self.args = OrderedDict()
+        # a list of Section
+        self.meta = []
+        # the current section
+        self.section = self.body
+        # associated expression and info (set by expression parser)
+        self.expr = None
+        self.info = None
+
+    def has_meta(self, name):
+        """Returns True if the doc has a meta section 'name'"""
+        for i in self.meta:
+            if i.name == name:
+                return True
+        return False
+
+    def append(self, line):
+        """Adds a # comment line, to be parsed and added to current section"""
+        line = line[1:]
+        if not line:
+            self._append_freeform(line)
+            return
+
+        if line[0] != ' ':
+            raise QAPISchemaError(self.parser, "missing space after #")
+        line = line[1:]
+
+        if self.symbol:
+            self._append_symbol_line(line)
+        elif (not self.body.content and
+              line.startswith("@") and line.endswith(":")):
+            self.symbol = line[1:-1]
+            if not self.symbol:
+                raise QAPISchemaError(self.parser, "Invalid symbol")
+        else:
+            self._append_freeform(line)
+
+    def _append_symbol_line(self, line):
+        name = line.split(' ', 1)[0]
+
+        if name.startswith("@") and name.endswith(":"):
+            line = line[len(name)+1:]
+            self._start_args_section(name[1:-1])
+        elif name in ("Returns:", "Since:",
+                      # those are often singular or plural
+                      "Note:", "Notes:",
+                      "Example:", "Examples:"):
+            line = line[len(name)+1:]
+            self._start_meta_section(name[:-1])
+
+        self._append_freeform(line)
+
+    def _start_args_section(self, name):
+        if not name:
+            raise QAPISchemaError(self.parser, "Invalid argument name")
+        if name in self.args:
+            raise QAPISchemaError(self.parser, "'%s' arg duplicated" % name)
+        self.section = QAPIDoc.ArgSection(name)
+        self.args[name] = self.section
+
+    def _start_meta_section(self, name):
+        if name in ("Returns", "Since") and self.has_meta(name):
+            raise QAPISchemaError(self.parser,
+                                  "Duplicated '%s' section" % name)
+        self.section = QAPIDoc.Section(name)
+        self.meta.append(self.section)
+
+    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] \
+           and line and not line[0].isspace():
+            # an empty line followed by a non-indented
+            # comment ends the argument section
+            self.section = self.body
+            self._append_freeform(line)
+            return
+        if in_arg or not self.section.name.startswith("Example"):
+            line = line.strip()
+        self.section.append(line)
+
+
 class QAPISchemaParser(object):
 
     def __init__(self, fp, previously_included=[], incl_info=None):
@@ -137,11 +240,18 @@ class QAPISchemaParser(object):
         self.line = 1
         self.line_pos = 0
         self.exprs = []
+        self.docs = []
         self.accept()
 
         while self.tok is not None:
             info = {'file': fname, 'line': self.line,
                     'parent': self.incl_info}
+            if self.tok == '#' and self.val.startswith('##'):
+                doc = self.get_doc()
+                doc.info = info
+                self.docs.append(doc)
+                continue
+
             expr = self.get_expr(False)
             if isinstance(expr, dict) and "include" in expr:
                 if len(expr) != 1:
@@ -160,6 +270,7 @@ class QAPISchemaParser(object):
                         raise QAPILineError(info, "Inclusion loop for %s"
                                             % include)
                     inf = inf['parent']
+
                 # skip multiple include of the same file
                 if incl_abs_fname in previously_included:
                     continue
@@ -171,12 +282,38 @@ class QAPISchemaParser(object):
                 exprs_include = QAPISchemaParser(fobj, previously_included,
                                                  info)
                 self.exprs.extend(exprs_include.exprs)
+                self.docs.extend(exprs_include.docs)
             else:
                 expr_elem = {'expr': expr,
                              'info': info}
+                if self.docs and not self.docs[-1].expr:
+                    self.docs[-1].expr = expr
+                    expr_elem['doc'] = self.docs[-1]
+
                 self.exprs.append(expr_elem)
 
-    def accept(self):
+    def get_doc(self):
+        if self.val != '##':
+            raise QAPISchemaError(self, "Junk after '##' at start of "
+                                  "documentation comment")
+
+        doc = QAPIDoc(self)
+        self.accept(False)
+        while self.tok == '#':
+            if self.val.startswith('##'):
+                # End of doc comment
+                if self.val != '##':
+                    raise QAPISchemaError(self, "Junk after '##' at end of "
+                                          "documentation comment")
+                self.accept()
+                return doc
+            else:
+                doc.append(self.val)
+            self.accept(False)
+
+        raise QAPISchemaError(self, "Documentation comment must end with '##'")
+
+    def accept(self, skip_comment=True):
         while True:
             self.tok = self.src[self.cursor]
             self.pos = self.cursor
@@ -184,7 +321,13 @@ class QAPISchemaParser(object):
             self.val = None
 
             if self.tok == '#':
+                if self.src[self.cursor] == '#':
+                    # Start of doc comment
+                    skip_comment = False
                 self.cursor = self.src.find('\n', self.cursor)
+                if not skip_comment:
+                    self.val = self.src[self.pos:self.cursor]
+                    return
             elif self.tok in "{}:,[]":
                 return
             elif self.tok == "'":
@@ -713,7 +856,7 @@ def check_keys(expr_elem, meta, required, optional=[]):
                                 % (key, meta, name))
 
 
-def check_exprs(exprs):
+def check_exprs(exprs, strict_doc):
     global all_names
 
     # Learn the types and check for valid expression keys
@@ -722,6 +865,11 @@ def check_exprs(exprs):
     for expr_elem in exprs:
         expr = expr_elem['expr']
         info = expr_elem['info']
+
+        if strict_doc and 'doc' not in expr_elem:
+            raise QAPILineError(info,
+                                "Expression missing documentation comment")
+
         if 'enum' in expr:
             check_keys(expr_elem, 'enum', ['data'], ['prefix'])
             add_enum(expr['enum'], info, expr['data'])
@@ -780,6 +928,63 @@ def check_exprs(exprs):
     return exprs
 
 
+def check_simple_doc(doc):
+    if doc.symbol:
+        raise QAPILineError(doc.info,
+                            "'%s' documention is not followed by the definition"
+                            % doc.symbol)
+
+    body = str(doc.body)
+    if re.search(r'@\S+:', body, re.MULTILINE):
+        raise QAPILineError(doc.info,
+                            "Document body cannot contain @NAME: sections")
+
+
+def check_expr_doc(doc, expr, info):
+    for i in ('enum', 'union', 'alternate', 'struct', 'command', 'event'):
+        if i in expr:
+            meta = i
+            break
+
+    name = expr[meta]
+    if doc.symbol != name:
+        raise QAPILineError(info, "Definition of '%s' follows documentation"
+                            " for '%s'" % (name, doc.symbol))
+    if doc.has_meta('Returns') and 'command' not in expr:
+        raise QAPILineError(info, "Invalid return documentation")
+
+    doc_args = set(doc.args.keys())
+    if meta == 'union':
+        data = expr.get('base', [])
+    else:
+        data = expr.get('data', [])
+    if isinstance(data, dict):
+        data = data.keys()
+    args = set([name.strip('*') for name in data])
+    if meta == 'alternate' or \
+       (meta == 'union' and not expr.get('discriminator')):
+        args.add('type')
+    if not doc_args.issubset(args):
+        raise QAPILineError(info, "Members documentation is not a subset of"
+                            " API %r > %r" % (list(doc_args), list(args)))
+
+
+def check_docs(docs):
+    for doc in docs:
+        for section in doc.args.values() + doc.meta:
+            content = str(section)
+            if not content or content.isspace():
+                raise QAPILineError(doc.info,
+                                    "Empty doc section '%s'" % section.name)
+
+        if not doc.expr:
+            check_simple_doc(doc)
+        else:
+            check_expr_doc(doc, doc.expr, doc.info)
+
+    return docs
+
+
 #
 # Schema compiler frontend
 #
@@ -1249,9 +1454,11 @@ class QAPISchemaEvent(QAPISchemaEntity):
 
 
 class QAPISchema(object):
-    def __init__(self, fname):
+    def __init__(self, fname, strict_doc=False):
         try:
-            self.exprs = check_exprs(QAPISchemaParser(open(fname, "r")).exprs)
+            parser = QAPISchemaParser(open(fname, "r"))
+            self.exprs = check_exprs(parser.exprs, strict_doc)
+            self.docs = check_docs(parser.docs)
             self._entity_dict = {}
             self._predefining = True
             self._def_predefineds()
diff --git a/scripts/qapi2texi.py b/scripts/qapi2texi.py
new file mode 100755
index 0000000..0cec43a
--- /dev/null
+++ b/scripts/qapi2texi.py
@@ -0,0 +1,331 @@
+#!/usr/bin/env python
+# QAPI texi generator
+#
+# This work is licensed under the terms of the GNU LGPL, version 2+.
+# See the COPYING file in the top-level directory.
+"""This script produces the documentation of a qapi schema in texinfo format"""
+import re
+import sys
+
+import qapi
+
+COMMAND_FMT = """
+@deftypefn {type} {{{ret}}} {name} @
+{{{args}}}
+
+{body}
+
+@end deftypefn
+
+""".format
+
+ENUM_FMT = """
+@deftp Enum {name}
+
+{body}
+
+@end deftp
+
+""".format
+
+STRUCT_FMT = """
+@deftp {{{type}}} {name} @
+{{{attrs}}}
+
+{body}
+
+@end deftp
+
+""".format
+
+EXAMPLE_FMT = """@example
+{code}
+@end example
+""".format
+
+
+def subst_strong(doc):
+    """Replaces *foo* by @strong{foo}"""
+    return re.sub(r'\*([^_\n]+)\*', r'@emph{\1}', doc)
+
+
+def subst_emph(doc):
+    """Replaces _foo_ by @emph{foo}"""
+    return re.sub(r'\s_([^_\n]+)_\s', r' @emph{\1} ', doc)
+
+
+def subst_vars(doc):
+    """Replaces @var by @code{var}"""
+    return re.sub(r'@([\w-]+)', r'@code{\1}', doc)
+
+
+def subst_braces(doc):
+    """Replaces {} with @{ @}"""
+    return doc.replace("{", "@{").replace("}", "@}")
+
+
+def texi_example(doc):
+    """Format @example"""
+    doc = subst_braces(doc).strip('\n')
+    return EXAMPLE_FMT(code=doc)
+
+
+def texi_comment(doc):
+    """
+    Format a comment
+
+    Lines starting with:
+    - |: generates an @example
+    - =: generates @section
+    - ==: generates @subsection
+    - 1. or 1): generates an @enumerate @item
+    - o/*/-: generates an @itemize list
+    """
+    lines = []
+    doc = subst_braces(doc)
+    doc = subst_vars(doc)
+    doc = subst_emph(doc)
+    doc = subst_strong(doc)
+    inlist = ""
+    lastempty = False
+    for line in doc.split('\n'):
+        empty = line == ""
+
+        if line.startswith("| "):
+            line = EXAMPLE_FMT(code=line[2:])
+        elif line.startswith("= "):
+            line = "@section " + line[2:]
+        elif line.startswith("== "):
+            line = "@subsection " + line[3:]
+        elif re.match("^([0-9]*[.)]) ", line):
+            if not inlist:
+                lines.append("@enumerate")
+                inlist = "enumerate"
+            line = line[line.find(" ")+1:]
+            lines.append("@item")
+        elif re.match("^[o*-] ", line):
+            if not inlist:
+                lines.append("@itemize %s" % {'o': "@bullet",
+                                              '*': "@minus",
+                                              '-': ""}[line[0]])
+                inlist = "itemize"
+            lines.append("@item")
+            line = line[2:]
+        elif lastempty and inlist:
+            lines.append("@end %s\n" % inlist)
+            inlist = ""
+
+        lastempty = empty
+        lines.append(line)
+
+    if inlist:
+        lines.append("@end %s\n" % inlist)
+    return "\n".join(lines)
+
+
+def texi_args(expr, key="data"):
+    """
+    Format the functions/structure/events.. arguments/members
+    """
+    if key not in expr:
+        return ""
+
+    args = expr[key]
+    arg_list = []
+    if isinstance(args, str):
+        arg_list.append(args)
+    else:
+        for name, typ in args.iteritems():
+            # optional arg
+            if name.startswith("*"):
+                name = name[1:]
+                arg_list.append("['%s': @var{%s}]" % (name, typ))
+            # regular arg
+            else:
+                arg_list.append("'%s': @var{%s}" % (name, typ))
+
+    return ", ".join(arg_list)
+
+
+def texi_body(doc):
+    """
+    Format the body of a symbol documentation:
+    - a table of arguments
+    - followed by "Returns/Notes/Since/Example" sections
+    """
+    def _section_order(section):
+        return {"Returns": 0,
+                "Note": 1,
+                "Notes": 1,
+                "Since": 2,
+                "Example": 3,
+                "Examples": 3}[section]
+
+    body = "@table @asis\n"
+    for arg, section in doc.args.iteritems():
+        desc = str(section)
+        opt = ''
+        if desc.startswith("#optional"):
+            desc = desc[10:]
+            opt = ' *'
+        elif desc.endswith("#optional"):
+            desc = desc[:-10]
+            opt = ' *'
+        body += "@item @code{'%s'}%s\n%s\n" % (arg, opt, texi_comment(desc))
+    body += "@end table\n"
+    body += texi_comment(str(doc.body))
+
+    meta = sorted(doc.meta, key=lambda i: _section_order(i.name))
+    for section in meta:
+        name, doc = (section.name, str(section))
+        func = texi_comment
+        if name.startswith("Example"):
+            func = texi_example
+
+        body += "\n@quotation %s\n%s\n@end quotation" % \
+                (name, func(doc))
+    return body
+
+
+def texi_alternate(expr, doc):
+    """
+    Format an alternate to texi
+    """
+    args = texi_args(expr)
+    body = texi_body(doc)
+    return STRUCT_FMT(type="Alternate",
+                      name=doc.symbol,
+                      attrs="[ " + args + " ]",
+                      body=body)
+
+
+def texi_union(expr, doc):
+    """
+    Format an union to texi
+    """
+    attrs = "@{ " + texi_args(expr, "base") + " @}"
+    discriminator = expr.get("discriminator")
+    if not discriminator:
+        union = "Flat Union"
+        discriminator = "type"
+    else:
+        union = "Union"
+    attrs += " + '%s' = [ " % discriminator
+    attrs += texi_args(expr, "data") + " ]"
+    body = texi_body(doc)
+
+    return STRUCT_FMT(type=union,
+                      name=doc.symbol,
+                      attrs=attrs,
+                      body=body)
+
+
+def texi_enum(expr, doc):
+    """
+    Format an enum to texi
+    """
+    for i in expr['data']:
+        if i not in doc.args:
+            doc.args[i] = ''
+    body = texi_body(doc)
+    return ENUM_FMT(name=doc.symbol,
+                    body=body)
+
+
+def texi_struct(expr, doc):
+    """
+    Format a struct to texi
+    """
+    args = texi_args(expr)
+    body = texi_body(doc)
+    attrs = "@{ " + args + " @}"
+    base = expr.get("base")
+    if base:
+        attrs += " + %s" % base
+    return STRUCT_FMT(type="Struct",
+                      name=doc.symbol,
+                      attrs=attrs,
+                      body=body)
+
+
+def texi_command(expr, doc):
+    """
+    Format a command to texi
+    """
+    args = texi_args(expr)
+    ret = expr["returns"] if "returns" in expr else ""
+    body = texi_body(doc)
+    return COMMAND_FMT(type="Command",
+                       name=doc.symbol,
+                       ret=ret,
+                       args="(" + args + ")",
+                       body=body)
+
+
+def texi_event(expr, doc):
+    """
+    Format an event to texi
+    """
+    args = texi_args(expr)
+    body = texi_body(doc)
+    return COMMAND_FMT(type="Event",
+                       name=doc.symbol,
+                       ret="",
+                       args="(" + args + ")",
+                       body=body)
+
+
+def texi_expr(expr, doc):
+    """
+    Format an expr to texi
+    """
+    (kind, _) = expr.items()[0]
+
+    fmt = {"command": texi_command,
+           "struct": texi_struct,
+           "enum": texi_enum,
+           "union": texi_union,
+           "alternate": texi_alternate,
+           "event": texi_event}
+    try:
+        fmt = fmt[kind]
+    except KeyError:
+        raise ValueError("Unknown expression kind '%s'" % kind)
+
+    return fmt(expr, doc)
+
+
+def texi(docs):
+    """
+    Convert QAPI schema expressions to texi documentation
+    """
+    res = []
+    for doc in docs:
+        expr = doc.expr
+        if not expr:
+            res.append(texi_body(doc))
+            continue
+        try:
+            doc = texi_expr(expr, doc)
+            res.append(doc)
+        except:
+            print >>sys.stderr, "error at @%s" % doc.info
+            raise
+
+    return '\n'.join(res)
+
+
+def main(argv):
+    """
+    Takes schema argument, prints result to stdout
+    """
+    if len(argv) != 2:
+        print >>sys.stderr, "%s: need exactly 1 argument: SCHEMA" % argv[0]
+        sys.exit(1)
+
+    schema = qapi.QAPISchema(argv[1], strict_doc=True)
+    print texi(schema.docs)
+
+
+if __name__ == "__main__":
+    main(sys.argv)
diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index 2841c51..8bc963e 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -45,16 +45,13 @@ QAPI parser does not).  At present, there is no place where a QAPI
 schema requires the use of JSON numbers or null.
 
 Comments are allowed; anything between an unquoted # and the following
-newline is ignored.  Although there is not yet a documentation
-generator, a form of stylized comments has developed for consistently
-documenting details about an expression and when it was added to the
-schema.  The documentation is delimited between two lines of ##, then
-the first line names the expression, an optional overview is provided,
-then individual documentation about each member of 'data' is provided,
-and finally, a 'Since: x.y.z' tag lists the release that introduced
-the expression.  Optional members are tagged with the phrase
-'#optional', often with their default value; and extensions added
-after the expression was first released are also given a '(since
+newline is ignored.  The documentation is delimited between two lines
+of ##, then the first line names the expression, an optional overview
+is provided, then individual documentation about each member of 'data'
+is provided, and finally, a 'Since: x.y.z' tag lists the release that
+introduced the expression.  Optional members are tagged with the
+phrase '#optional', often with their default value; and extensions
+added after the expression was first released are also given a '(since
 x.y.z)' comment.  For example:
 
     ##
@@ -73,12 +70,49 @@ x.y.z)' comment.  For example:
     #           (Since 2.0)
     #
     # Since: 0.14.0
+    #
+    # Notes: You can also make a list:
+    #        - with items
+    #        - like this
+    #
+    # Example:
+    #
+    # -> { "execute": ... }
+    # <- { "return": ... }
+    #
     ##
     { 'struct': 'BlockStats',
       'data': {'*device': 'str', 'stats': 'BlockDeviceStats',
                '*parent': 'BlockStats',
                '*backing': 'BlockStats'} }
 
+It's also possible to create documentation sections, such as:
+
+    ##
+    # = Section
+    # == Subsection
+    #
+    # Some text foo with *strong* and _emphasis_
+    # 1. with a list
+    # 2. like that
+    #
+    # And some code:
+    # | $ echo foo
+    # | -> do this
+    # | <- get that
+    #
+    ##
+
+Text *foo* and _foo_ are for "strong" and "emphasis" styles (they do
+not work over multiple lines). @foo is used to reference a symbol.
+
+Lines starting with the following characters and a space:
+- | are examples
+- = are top section
+- == are subsection
+- X. or X) are enumerations (X is any number)
+- o/*/- are itemized list
+
 The schema sets up a series of types, as well as commands and events
 that will use those types.  Forward references are allowed: the parser
 scans in two passes, where the first pass learns all type names, and
diff --git a/tests/qapi-schema/doc-bad-args.err b/tests/qapi-schema/doc-bad-args.err
new file mode 100644
index 0000000..a55e003
--- /dev/null
+++ b/tests/qapi-schema/doc-bad-args.err
@@ -0,0 +1 @@
+tests/qapi-schema/doc-bad-args.json:1: Members documentation is not a subset of API ['a', 'b'] > ['a']
diff --git a/tests/qapi-schema/doc-bad-args.exit b/tests/qapi-schema/doc-bad-args.exit
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/qapi-schema/doc-bad-args.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/doc-bad-args.json b/tests/qapi-schema/doc-bad-args.json
new file mode 100644
index 0000000..26992ea
--- /dev/null
+++ b/tests/qapi-schema/doc-bad-args.json
@@ -0,0 +1,6 @@
+##
+# @foo:
+# @a: a
+# @b: b
+##
+{ 'command': 'foo', 'data': {'a': 'int'} }
diff --git a/tests/qapi-schema/doc-bad-args.out b/tests/qapi-schema/doc-bad-args.out
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/doc-bad-symbol.err b/tests/qapi-schema/doc-bad-symbol.err
new file mode 100644
index 0000000..9c969d1
--- /dev/null
+++ b/tests/qapi-schema/doc-bad-symbol.err
@@ -0,0 +1 @@
+tests/qapi-schema/doc-bad-symbol.json:1: Definition of 'foo' follows documentation for 'food'
diff --git a/tests/qapi-schema/doc-bad-symbol.exit b/tests/qapi-schema/doc-bad-symbol.exit
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/qapi-schema/doc-bad-symbol.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/doc-bad-symbol.json b/tests/qapi-schema/doc-bad-symbol.json
new file mode 100644
index 0000000..7255152
--- /dev/null
+++ b/tests/qapi-schema/doc-bad-symbol.json
@@ -0,0 +1,4 @@
+##
+# @food:
+##
+{ 'command': 'foo', 'data': {'a': 'int'} }
diff --git a/tests/qapi-schema/doc-bad-symbol.out b/tests/qapi-schema/doc-bad-symbol.out
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/doc-duplicated-arg.err b/tests/qapi-schema/doc-duplicated-arg.err
new file mode 100644
index 0000000..88a272b
--- /dev/null
+++ b/tests/qapi-schema/doc-duplicated-arg.err
@@ -0,0 +1 @@
+tests/qapi-schema/doc-duplicated-arg.json:4:1: 'a' arg duplicated
diff --git a/tests/qapi-schema/doc-duplicated-arg.exit b/tests/qapi-schema/doc-duplicated-arg.exit
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/qapi-schema/doc-duplicated-arg.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/doc-duplicated-arg.json b/tests/qapi-schema/doc-duplicated-arg.json
new file mode 100644
index 0000000..f7804c2
--- /dev/null
+++ b/tests/qapi-schema/doc-duplicated-arg.json
@@ -0,0 +1,5 @@
+##
+# @foo:
+# @a:
+# @a:
+##
diff --git a/tests/qapi-schema/doc-duplicated-arg.out b/tests/qapi-schema/doc-duplicated-arg.out
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/doc-duplicated-return.err b/tests/qapi-schema/doc-duplicated-return.err
new file mode 100644
index 0000000..1b02880
--- /dev/null
+++ b/tests/qapi-schema/doc-duplicated-return.err
@@ -0,0 +1 @@
+tests/qapi-schema/doc-duplicated-return.json:5:1: Duplicated 'Returns' section
diff --git a/tests/qapi-schema/doc-duplicated-return.exit b/tests/qapi-schema/doc-duplicated-return.exit
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/qapi-schema/doc-duplicated-return.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/doc-duplicated-return.json b/tests/qapi-schema/doc-duplicated-return.json
new file mode 100644
index 0000000..de7234b
--- /dev/null
+++ b/tests/qapi-schema/doc-duplicated-return.json
@@ -0,0 +1,6 @@
+##
+# @foo:
+#
+# Returns: 0
+# Returns: 1
+##
diff --git a/tests/qapi-schema/doc-duplicated-return.out b/tests/qapi-schema/doc-duplicated-return.out
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/doc-duplicated-since.err b/tests/qapi-schema/doc-duplicated-since.err
new file mode 100644
index 0000000..72efb2a
--- /dev/null
+++ b/tests/qapi-schema/doc-duplicated-since.err
@@ -0,0 +1 @@
+tests/qapi-schema/doc-duplicated-since.json:5:1: Duplicated 'Since' section
diff --git a/tests/qapi-schema/doc-duplicated-since.exit b/tests/qapi-schema/doc-duplicated-since.exit
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/qapi-schema/doc-duplicated-since.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/doc-duplicated-since.json b/tests/qapi-schema/doc-duplicated-since.json
new file mode 100644
index 0000000..da261a1
--- /dev/null
+++ b/tests/qapi-schema/doc-duplicated-since.json
@@ -0,0 +1,6 @@
+##
+# @foo:
+#
+# Since: 0
+# Since: 1
+##
diff --git a/tests/qapi-schema/doc-duplicated-since.out b/tests/qapi-schema/doc-duplicated-since.out
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/doc-empty-arg.err b/tests/qapi-schema/doc-empty-arg.err
new file mode 100644
index 0000000..0647eed
--- /dev/null
+++ b/tests/qapi-schema/doc-empty-arg.err
@@ -0,0 +1 @@
+tests/qapi-schema/doc-empty-arg.json:3:1: Invalid argument name
diff --git a/tests/qapi-schema/doc-empty-arg.exit b/tests/qapi-schema/doc-empty-arg.exit
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/qapi-schema/doc-empty-arg.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/doc-empty-arg.json b/tests/qapi-schema/doc-empty-arg.json
new file mode 100644
index 0000000..74f526c
--- /dev/null
+++ b/tests/qapi-schema/doc-empty-arg.json
@@ -0,0 +1,4 @@
+##
+# @foo:
+# @:
+##
diff --git a/tests/qapi-schema/doc-empty-arg.out b/tests/qapi-schema/doc-empty-arg.out
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/doc-empty-section.err b/tests/qapi-schema/doc-empty-section.err
new file mode 100644
index 0000000..c940332
--- /dev/null
+++ b/tests/qapi-schema/doc-empty-section.err
@@ -0,0 +1 @@
+tests/qapi-schema/doc-empty-section.json:1: Empty doc section 'Note'
diff --git a/tests/qapi-schema/doc-empty-section.exit b/tests/qapi-schema/doc-empty-section.exit
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/qapi-schema/doc-empty-section.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/doc-empty-section.json b/tests/qapi-schema/doc-empty-section.json
new file mode 100644
index 0000000..ed3867d
--- /dev/null
+++ b/tests/qapi-schema/doc-empty-section.json
@@ -0,0 +1,6 @@
+##
+# @foo:
+#
+# Note:
+##
+{ 'command': 'foo', 'data': {'a': 'int'} }
diff --git a/tests/qapi-schema/doc-empty-section.out b/tests/qapi-schema/doc-empty-section.out
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/doc-empty-symbol.err b/tests/qapi-schema/doc-empty-symbol.err
new file mode 100644
index 0000000..955dc3c
--- /dev/null
+++ b/tests/qapi-schema/doc-empty-symbol.err
@@ -0,0 +1 @@
+tests/qapi-schema/doc-empty-symbol.json:2:1: Invalid symbol
diff --git a/tests/qapi-schema/doc-empty-symbol.exit b/tests/qapi-schema/doc-empty-symbol.exit
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/qapi-schema/doc-empty-symbol.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/doc-empty-symbol.json b/tests/qapi-schema/doc-empty-symbol.json
new file mode 100644
index 0000000..8a2d662
--- /dev/null
+++ b/tests/qapi-schema/doc-empty-symbol.json
@@ -0,0 +1,3 @@
+##
+# @:
+##
diff --git a/tests/qapi-schema/doc-empty-symbol.out b/tests/qapi-schema/doc-empty-symbol.out
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/doc-invalid-end.err b/tests/qapi-schema/doc-invalid-end.err
new file mode 100644
index 0000000..5ed8911
--- /dev/null
+++ b/tests/qapi-schema/doc-invalid-end.err
@@ -0,0 +1 @@
+tests/qapi-schema/doc-invalid-end.json:3:2: Documentation comment must end with '##'
diff --git a/tests/qapi-schema/doc-invalid-end.exit b/tests/qapi-schema/doc-invalid-end.exit
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/qapi-schema/doc-invalid-end.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/doc-invalid-end.json b/tests/qapi-schema/doc-invalid-end.json
new file mode 100644
index 0000000..7452718
--- /dev/null
+++ b/tests/qapi-schema/doc-invalid-end.json
@@ -0,0 +1,3 @@
+##
+# An invalid comment
+#
diff --git a/tests/qapi-schema/doc-invalid-end.out b/tests/qapi-schema/doc-invalid-end.out
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/doc-invalid-end2.err b/tests/qapi-schema/doc-invalid-end2.err
new file mode 100644
index 0000000..acd23bb
--- /dev/null
+++ b/tests/qapi-schema/doc-invalid-end2.err
@@ -0,0 +1 @@
+tests/qapi-schema/doc-invalid-end2.json:3:1: Junk after '##' at end of documentation comment
diff --git a/tests/qapi-schema/doc-invalid-end2.exit b/tests/qapi-schema/doc-invalid-end2.exit
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/qapi-schema/doc-invalid-end2.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/doc-invalid-end2.json b/tests/qapi-schema/doc-invalid-end2.json
new file mode 100644
index 0000000..12e669e
--- /dev/null
+++ b/tests/qapi-schema/doc-invalid-end2.json
@@ -0,0 +1,3 @@
+##
+#
+## invalid
diff --git a/tests/qapi-schema/doc-invalid-end2.out b/tests/qapi-schema/doc-invalid-end2.out
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/doc-invalid-return.err b/tests/qapi-schema/doc-invalid-return.err
new file mode 100644
index 0000000..c3f2691
--- /dev/null
+++ b/tests/qapi-schema/doc-invalid-return.err
@@ -0,0 +1 @@
+tests/qapi-schema/doc-invalid-return.json:1: Invalid return documentation
diff --git a/tests/qapi-schema/doc-invalid-return.exit b/tests/qapi-schema/doc-invalid-return.exit
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/qapi-schema/doc-invalid-return.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/doc-invalid-return.json b/tests/qapi-schema/doc-invalid-return.json
new file mode 100644
index 0000000..6568b6d
--- /dev/null
+++ b/tests/qapi-schema/doc-invalid-return.json
@@ -0,0 +1,5 @@
+##
+# @foo:
+# Returns: blah
+##
+{ 'event': 'foo' }
diff --git a/tests/qapi-schema/doc-invalid-return.out b/tests/qapi-schema/doc-invalid-return.out
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/doc-invalid-section.err b/tests/qapi-schema/doc-invalid-section.err
new file mode 100644
index 0000000..f4c12aa
--- /dev/null
+++ b/tests/qapi-schema/doc-invalid-section.err
@@ -0,0 +1 @@
+tests/qapi-schema/doc-invalid-section.json:1: Document body cannot contain @NAME: sections
diff --git a/tests/qapi-schema/doc-invalid-section.exit b/tests/qapi-schema/doc-invalid-section.exit
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/qapi-schema/doc-invalid-section.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/doc-invalid-section.json b/tests/qapi-schema/doc-invalid-section.json
new file mode 100644
index 0000000..9afa2f1
--- /dev/null
+++ b/tests/qapi-schema/doc-invalid-section.json
@@ -0,0 +1,4 @@
+##
+# freeform
+# @note: foo
+##
diff --git a/tests/qapi-schema/doc-invalid-section.out b/tests/qapi-schema/doc-invalid-section.out
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/doc-invalid-start.err b/tests/qapi-schema/doc-invalid-start.err
new file mode 100644
index 0000000..194c8d7
--- /dev/null
+++ b/tests/qapi-schema/doc-invalid-start.err
@@ -0,0 +1 @@
+tests/qapi-schema/doc-invalid-start.json:1:1: Junk after '##' at start of documentation comment
diff --git a/tests/qapi-schema/doc-invalid-start.exit b/tests/qapi-schema/doc-invalid-start.exit
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/qapi-schema/doc-invalid-start.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/doc-invalid-start.json b/tests/qapi-schema/doc-invalid-start.json
new file mode 100644
index 0000000..f85adfd
--- /dev/null
+++ b/tests/qapi-schema/doc-invalid-start.json
@@ -0,0 +1,3 @@
+## invalid
+#
+##
diff --git a/tests/qapi-schema/doc-invalid-start.out b/tests/qapi-schema/doc-invalid-start.out
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/doc-missing-expr.err b/tests/qapi-schema/doc-missing-expr.err
new file mode 100644
index 0000000..e4ed135
--- /dev/null
+++ b/tests/qapi-schema/doc-missing-expr.err
@@ -0,0 +1 @@
+tests/qapi-schema/doc-missing-expr.json:1: 'bar' documention is not followed by the definition
diff --git a/tests/qapi-schema/doc-missing-expr.exit b/tests/qapi-schema/doc-missing-expr.exit
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/qapi-schema/doc-missing-expr.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/doc-missing-expr.json b/tests/qapi-schema/doc-missing-expr.json
new file mode 100644
index 0000000..a0be2e1
--- /dev/null
+++ b/tests/qapi-schema/doc-missing-expr.json
@@ -0,0 +1,3 @@
+##
+# @bar:
+##
diff --git a/tests/qapi-schema/doc-missing-expr.out b/tests/qapi-schema/doc-missing-expr.out
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/doc-missing-space.err b/tests/qapi-schema/doc-missing-space.err
new file mode 100644
index 0000000..37056ce
--- /dev/null
+++ b/tests/qapi-schema/doc-missing-space.err
@@ -0,0 +1 @@
+tests/qapi-schema/doc-missing-space.json:3:1: missing space after #
diff --git a/tests/qapi-schema/doc-missing-space.exit b/tests/qapi-schema/doc-missing-space.exit
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/qapi-schema/doc-missing-space.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/doc-missing-space.json b/tests/qapi-schema/doc-missing-space.json
new file mode 100644
index 0000000..39303de
--- /dev/null
+++ b/tests/qapi-schema/doc-missing-space.json
@@ -0,0 +1,4 @@
+##
+# missing space:
+#wef
+##
diff --git a/tests/qapi-schema/doc-missing-space.out b/tests/qapi-schema/doc-missing-space.out
new file mode 100644
index 0000000..e69de29
diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index 1719463..d921ec4 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -3,6 +3,43 @@
 # This file is a stress test of supported qapi constructs that must
 # parse and compile correctly.
 
+##
+# = Section
+# == subsection
+#
+# Some text foo with *strong* and _emphasis_
+# 1. with a list
+# 2. like that @foo
+#
+# And some code:
+# | $ echo foo
+# | -> do this
+# | <- get that
+#
+# Note: is not a meta
+##
+
+##
+# @TestStruct:
+# @integer: foo
+#           blah
+#
+#           bao
+#
+# @boolean: bar
+# @string: baz
+#
+# body with @var
+#
+# Example:
+#
+# -> { "execute": ... }
+# <- { "return": ... }
+#
+# Since: 2.3
+# Note: a note
+#
+##
 { 'struct': 'TestStruct',
   'data': { 'integer': 'int', 'boolean': 'bool', 'string': 'str' } }
 
@@ -123,6 +160,27 @@
   'data': {'ud1a': 'UserDefOne', '*ud1b': 'UserDefOne'},
   'returns': 'UserDefTwo' }
 
+##
+# Another comment
+##
+
+##
+# @guest-get-time:
+#
+# @a: an integer
+# @b: #optional integer
+#
+# @guest-get-time body
+#
+# Returns: returns something
+#
+# Example:
+#
+# -> { "execute": "guest-get-time", ... }
+# <- { "return": "42" }
+#
+##
+
 # Returning a non-dictionary requires a name from the whitelist
 { 'command': 'guest-get-time', 'data': {'a': 'int', '*b': 'int' },
   'returns': 'int' }
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index 9d99c4e..fde9e06 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -232,3 +232,52 @@ command user_def_cmd1 q_obj_user_def_cmd1-arg -> None
    gen=True success_response=True boxed=False
 command user_def_cmd2 q_obj_user_def_cmd2-arg -> UserDefTwo
    gen=True success_response=True boxed=False
+doc freeform
+    body=
+= Section
+== subsection
+
+Some text foo with *strong* and _emphasis_
+1. with a list
+2. like that @foo
+
+And some code:
+| $ echo foo
+| -> do this
+| <- get that
+
+Note: is not a meta
+doc symbol=TestStruct expr=('struct', 'TestStruct')
+    arg=integer
+foo
+blah
+
+bao
+    arg=boolean
+bar
+    arg=string
+baz
+    meta=Example
+-> { "execute": ... }
+<- { "return": ... }
+    meta=Since
+2.3
+    meta=Note
+a note
+    body=
+body with @var
+doc freeform
+    body=
+Another comment
+doc symbol=guest-get-time expr=('command', 'guest-get-time')
+    arg=a
+an integer
+    arg=b
+#optional integer
+    meta=Returns
+returns something
+    meta=Example
+-> { "execute": "guest-get-time", ... }
+<- { "return": "42" }
+    body=
+@guest-get-time body
diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
index ef74e2c..22da014 100644
--- a/tests/qapi-schema/test-qapi.py
+++ b/tests/qapi-schema/test-qapi.py
@@ -55,3 +55,15 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
 
 schema = QAPISchema(sys.argv[1])
 schema.visit(QAPISchemaTestVisitor())
+
+for doc in schema.docs:
+    if doc.symbol:
+        print 'doc symbol=%s expr=%s' % \
+            (doc.symbol, doc.expr.items()[0])
+    else:
+        print 'doc freeform'
+    for arg, section in doc.args.iteritems():
+        print '    arg=%s\n%s' % (arg, section)
+    for section in doc.meta:
+        print '    meta=%s\n%s' % (section.name, section)
+    print '    body=\n%s' % doc.body
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [Qemu-devel] [PATCH v5 14/17] texi2pod: learn quotation, deftp and deftypefn
  2016-11-17 15:54 [Qemu-devel] [PATCH v5 00/17] qapi doc generation (whole version, squashed) Marc-André Lureau
                   ` (12 preceding siblings ...)
  2016-11-17 15:55 ` [Qemu-devel] [PATCH v5 13/17] qapi: add qapi2texi script Marc-André Lureau
@ 2016-11-17 15:55 ` Marc-André Lureau
  2016-11-17 15:55 ` [Qemu-devel] [PATCH v5 15/17] (SQUASHED) move doc to schema Marc-André Lureau
                   ` (3 subsequent siblings)
  17 siblings, 0 replies; 48+ messages in thread
From: Marc-André Lureau @ 2016-11-17 15:55 UTC (permalink / raw)
  To: qemu-devel; +Cc: eblake, armbru, Marc-André Lureau

Learn a few more markups used for API documentation.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 scripts/texi2pod.pl | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 50 insertions(+), 3 deletions(-)

diff --git a/scripts/texi2pod.pl b/scripts/texi2pod.pl
index 8767662..fe0d503 100755
--- a/scripts/texi2pod.pl
+++ b/scripts/texi2pod.pl
@@ -37,6 +37,7 @@ $inf = "";
 $ibase = "";
 @ipath = ();
 $encoding = undef;
+@args = ();
 
 while ($_ = shift) {
     if (/^-D(.*)$/) {
@@ -162,7 +163,8 @@ while(<$inf>) {
 	if ($ended =~ /^(?:ifset|ifclear|ignore|menu|iftex)$/) {
 	    $skipping = pop @skstack;
 	    next;
-	} elsif ($ended =~ /^(?:example|smallexample|display)$/) {
+	} elsif ($ended =~ /^(?:example|smallexample|display
+                            |quotation|deftp|deftypefn)$/x) {
 	    $shift = "";
 	    $_ = "";	# need a paragraph break
 	} elsif ($ended =~ /^(?:itemize|enumerate|[fv]?table)$/) {
@@ -303,6 +305,7 @@ while(<$inf>) {
 	$ic =~ s/\@(?:code|kbd)/C/;
 	$ic =~ s/\@(?:dfn|var|emph|cite|i)/I/;
 	$ic =~ s/\@(?:file)/F/;
+	$ic =~ s/\@(?:asis)//;
 	$_ = "\n=over 4\n";
     };
 
@@ -323,10 +326,54 @@ while(<$inf>) {
 	$_ = "\n=item ".join (" : ", @columns)."\n";
     };
 
+    /^\@(quotation)\s*(.+)?$/ and do {
+        push @endwstack, $endw;
+        $endw = $1;
+        $_ = "\n$2:"
+    };
+
+    /^{(.*)}$|^(.*)$/ and $#args > 0 and do {
+        $kind = $args[0];
+        $arguments = $1 // "";
+        if ($endw eq "deftypefn") {
+            $ret = $args[1];
+            $fname = "B<$args[2]>";
+            $_ = $ret ? "$ret " : "";
+            $_ .= "$fname $arguments ($kind)";
+        } else {
+            $_ = "B<$args[1]> ($kind)\n\n$arguments";
+        }
+        @args = ();
+    };
+
+    /^\@(deftp)\s*(.+)?$/ and do {
+        push @endwstack, $endw;
+        $endw = $1;
+        $arg = $2;
+        $arg =~ s/{([^}]*)}/$1/g;
+        $arg =~ s/\@$//;
+        @args = split (/ /, $arg);
+        $_ = "";
+    };
+
+    /^\@(deftypefn)\s*(.+)?$/ and do {
+        push @endwstack, $endw;
+        $endw = $1;
+        $arg = $2;
+        $arg =~ s/{([^}]*)}/$1/g;
+        $arg =~ s/\@$//;
+        @args = split (/ /, $arg);
+        $_ = "";
+    };
+
     /^\@itemx?\s*(.+)?$/ and do {
 	if (defined $1) {
-	    # Entity escapes prevent munging by the <> processing below.
-	    $_ = "\n=item $ic\&LT;$1\&GT;\n";
+            if ($ic eq "") {
+                $_ = "\n=item $1\n";
+            } else {
+                # Entity escapes prevent munging by the <> processing below.
+                $_ = "\n=item $ic\&LT;$1\&GT;\n";
+            }
 	} else {
 	    $_ = "\n=item $ic\n";
 	    $ic =~ y/A-Ya-y/B-Zb-z/;
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [Qemu-devel] [PATCH v5 15/17] (SQUASHED) move doc to schema
  2016-11-17 15:54 [Qemu-devel] [PATCH v5 00/17] qapi doc generation (whole version, squashed) Marc-André Lureau
                   ` (13 preceding siblings ...)
  2016-11-17 15:55 ` [Qemu-devel] [PATCH v5 14/17] texi2pod: learn quotation, deftp and deftypefn Marc-André Lureau
@ 2016-11-17 15:55 ` Marc-André Lureau
  2016-11-17 15:55 ` [Qemu-devel] [PATCH v5 16/17] docs: add qemu logo Marc-André Lureau
                   ` (2 subsequent siblings)
  17 siblings, 0 replies; 48+ messages in thread
From: Marc-André Lureau @ 2016-11-17 15:55 UTC (permalink / raw)
  To: qemu-devel; +Cc: eblake, armbru, Marc-André Lureau

qmp-commands: move 'add_client' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-name' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-kvm' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-status' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-uuid' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-chardev' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-chardev-backends' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'ringbuf-write' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'ringbuf-read' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-events' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-migrate' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'migrate-set-capabilities' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-migrate-capabilities' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'migrate-set-parameters' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-migrate-parameters' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'client_migrate_info' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'migrate-start-postcopy' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-mice' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-cpus' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-iothreads' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-vnc' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-spice' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-balloon' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-pci' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'quit' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'stop' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'system_reset' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'system_powerdown' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'cpu-add' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'add_client' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-name' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-kvm' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-status' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-uuid' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-chardev' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-chardev-backends' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'ringbuf-write' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'ringbuf-read' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-events' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-migrate' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'migrate-set-capabilities' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-migrate-capabilities' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'migrate-set-parameters' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-migrate-parameters' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'client_migrate_info' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'migrate-start-postcopy' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-mice' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-cpus' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-iothreads' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-vnc' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-spice' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-balloon' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-pci' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'quit' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'stop' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'system_reset' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'system_powerdown' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'cpu-add' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'memsave' doc to schema

Notice that "cpu" argument is actually "cpu-index" in the json.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'pmemsave' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'cont' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'system_wakeup' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'inject-nmi' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'set_link' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'balloon' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'transaction' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'human-monitor-command' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'migrate_cancel' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'migrate_set_downtime' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'migrate_set_speed' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-migrate-cache-size' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'set_password' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'expire_password' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'change' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'migrate' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'migrate-incoming' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'xen-save-devices-state' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'xen-set-global-dirty-log' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'device_del' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'dump-guest-memory' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-dump-guest-memory-capability' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'dump-skeys' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'netdev_add' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'netdev_del' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'object-add' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'object-del' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'getfd' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'closefd' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'add-fd' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'remove-fd' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-fdsets' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'send-key' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'screendump' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'chardev-add' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'chardev-remove' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-tpm-models' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-tpm-types' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-tpm' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-command-line-options' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-rx-filter' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'input-send-event' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-memdev' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-memory-devices' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-acpi-ospm-status' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'rtc-reset-reinjection' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-block' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-blockstats' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'block_passwd' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'block_resize' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'blockdev-snapshot-sync' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'blockdev-snapshot' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'change-backing-file' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'block-commit' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'drive-backup' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'blockdev-backup' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-named-block-nodes' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'drive-mirror' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'block-dirty-bitmap-add' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'block-dirty-bitmap-remove' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'block-dirty-bitmap-clear' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'block_set_io_throttle' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'block-stream' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'blockdev-add' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'x-blockdev-del' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'blockdev-open-tray' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'blockdev-close-tray' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'x-blockdev-remove-medium' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'x-blockdev-insert-medium' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'blockdev-change-medium' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'block-set-write-threshold' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'blockdev-snapshot-internal-sync' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'blockdev-snapshot-delete-internal-sync' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'eject' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-version' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-commands' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'add_client' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-name' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-kvm' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-status' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-uuid' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-chardev' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-chardev-backends' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'ringbuf-write' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'ringbuf-read' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-events' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-migrate' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'migrate-set-capabilities' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-migrate-capabilities' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'migrate-set-parameters' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-migrate-parameters' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'client_migrate_info' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'migrate-start-postcopy' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-mice' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-cpus' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-iothreads' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-vnc' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-spice' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-balloon' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-pci' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'quit' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'stop' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'system_reset' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'system_powerdown' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'cpu-add' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'memsave' doc to schema

Notice that "cpu" argument is actually "cpu-index" in the json.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'pmemsave' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'cont' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'system_wakeup' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'inject-nmi' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'set_link' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'balloon' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'transaction' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'human-monitor-command' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'migrate_cancel' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'migrate_set_downtime' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'migrate_set_speed' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-migrate-cache-size' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'set_password' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'expire_password' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'change' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'migrate' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'migrate-incoming' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'xen-save-devices-state' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'xen-set-global-dirty-log' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'device_del' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'dump-guest-memory' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-dump-guest-memory-capability' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'dump-skeys' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'netdev_add' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'netdev_del' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'object-add' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'object-del' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'getfd' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'closefd' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'add-fd' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'remove-fd' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-fdsets' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'send-key' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'screendump' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'chardev-add' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'chardev-remove' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-tpm-models' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-tpm-types' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-tpm' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-command-line-options' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-rx-filter' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'input-send-event' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-memdev' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-memory-devices' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-acpi-ospm-status' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'rtc-reset-reinjection' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-block' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-blockstats' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'block_passwd' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'block_resize' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'blockdev-snapshot-sync' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'blockdev-snapshot' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'change-backing-file' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'block-commit' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'drive-backup' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'blockdev-backup' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-named-block-nodes' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'drive-mirror' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'block-dirty-bitmap-add' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'block-dirty-bitmap-remove' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'block-dirty-bitmap-clear' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'block_set_io_throttle' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'block-stream' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'blockdev-add' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'x-blockdev-del' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'blockdev-open-tray' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'blockdev-close-tray' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'x-blockdev-remove-medium' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'x-blockdev-insert-medium' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'blockdev-change-medium' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'block-set-write-threshold' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'blockdev-snapshot-internal-sync' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'blockdev-snapshot-delete-internal-sync' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'eject' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-version' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-commands' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-rocker' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-rocker-ports' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-rocker-of-dpa-flows' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-rocker-of-dpa-groups' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'trace-event-get-state' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'trace-event-set-state' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-hotpluggable-cpus' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-gic-capabilities' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'x-blockdev-change' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'blockdev-mirror' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'query-dump' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'migrate-set-cache-size' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'xen-load-devices-state' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move 'x-colo-lost-heartbeat' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: remove query-qmp-schema from txt

It's better described in the schema already.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: remove qmp_capabilities from txt

It's better described in the schema already.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: remove cpu from txt

The cpu command is deprecated since commit 755f196898.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: remove device_add from txt

It's already fully described in the schema. Fix the schema to document
the command and not the argument (place doc before first arg).

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-commands: move documentation bits to schema

Moving the remaining bits of documentation to the json
file (text improvements is not the objective of this patch)

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-events: move 'BLOCK_IMAGE_CORRUPTED' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-events: move 'BLOCK_IO_ERROR' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-events: move 'BLOCK_JOB_COMPLETED' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-events: move 'BLOCK_JOB_CANCELLED' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-events: move 'BLOCK_JOB_ERROR' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-events: move 'BLOCK_JOB_READY' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-events: move 'DEVICE_TRAY_MOVED' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-events: move 'SHUTDOWN' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-events: move 'POWERDOWN' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-events: move 'RESET' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-events: move 'STOP' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-events: move 'RESUME' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-events: move 'SUSPEND' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-events: move 'SUSPEND_DISK' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-events: move 'WAKEUP' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-events: move 'RTC_CHANGE' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-events: move 'WATCHDOG' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-events: move 'DEVICE_DELETED' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-events: move 'NIC_RX_FILTER_CHANGED' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-events: move 'VNC_CONNECTED' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-events: move 'VNC_INITIALIZED' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-events: move 'VNC_DISCONNECTED' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-events: move 'SPICE_CONNECTED' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-events: move 'SPICE_INITIALIZED' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-events: move 'SPICE_DISCONNECTED' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-events: move 'SPICE_MIGRATE_COMPLETED' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-events: move 'MIGRATION' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-events: move 'ACPI_DEVICE_OST' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-events: move 'BALLOON_CHANGE' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-events: move 'GUEST_PANICKED' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-events: move 'QUORUM_FAILURE' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-events: move 'QUORUM_REPORT_BAD' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-events: move 'VSERPORT_CHANGE' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-events: move 'MEM_UNPLUG_ERROR' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-events: move 'DUMP_COMPLETED' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

qmp-events: move 'MIGRATION_PASS' doc to schema

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 Makefile              |    1 -
 docs/qmp-commands.txt | 3824 -------------------------------------------------
 docs/qmp-events.txt   |  731 ----------
 qapi-schema.json      | 1279 ++++++++++++++++-
 qapi/block-core.json  |  679 ++++++++-
 qapi/block.json       |   56 +-
 qapi/common.json      |   32 +
 qapi/event.json       |  240 ++++
 qapi/rocker.json      |   57 +-
 qapi/trace.json       |   14 +
 10 files changed, 2309 insertions(+), 4604 deletions(-)
 delete mode 100644 docs/qmp-commands.txt
 delete mode 100644 docs/qmp-events.txt

diff --git a/Makefile b/Makefile
index 474cc5e..3617736 100644
--- a/Makefile
+++ b/Makefile
@@ -431,7 +431,6 @@ endif
 install-doc: $(DOCS)
 	$(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)"
 	$(INSTALL_DATA) qemu-doc.html "$(DESTDIR)$(qemu_docdir)"
-	$(INSTALL_DATA) $(SRC_PATH)/docs/qmp-commands.txt "$(DESTDIR)$(qemu_docdir)"
 ifdef CONFIG_POSIX
 	$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1"
 	$(INSTALL_DATA) qemu.1 "$(DESTDIR)$(mandir)/man1"
diff --git a/docs/qmp-commands.txt b/docs/qmp-commands.txt
deleted file mode 100644
index abf210a..0000000
--- a/docs/qmp-commands.txt
+++ /dev/null
@@ -1,3824 +0,0 @@
-                        QMP Supported Commands
-                        ----------------------
-
-This document describes all commands currently supported by QMP.
-
-Most of the time their usage is exactly the same as in the user Monitor, this
-means that any other document which also describe commands (the manpage,
-QEMU's manual, etc) can and should be consulted.
-
-QMP has two types of commands: regular and query commands. Regular commands
-usually change the Virtual Machine's state someway, while query commands just
-return information. The sections below are divided accordingly.
-
-It's important to observe that all communication examples are formatted in
-a reader-friendly way, so that they're easier to understand. However, in real
-protocol usage, they're emitted as a single line.
-
-Also, the following notation is used to denote data flow:
-
--> data issued by the Client
-<- Server data response
-
-Please, refer to the QMP specification (docs/qmp-spec.txt) for detailed
-information on the Server command and response formats.
-
-NOTE: This document is temporary and will be replaced soon.
-
-1. Stability Considerations
-===========================
-
-The current QMP command set (described in this file) may be useful for a
-number of use cases, however it's limited and several commands have bad
-defined semantics, specially with regard to command completion.
-
-These problems are going to be solved incrementally in the next QEMU releases
-and we're going to establish a deprecation policy for badly defined commands.
-
-If you're planning to adopt QMP, please observe the following:
-
-    1. The deprecation policy will take effect and be documented soon, please
-       check the documentation of each used command as soon as a new release of
-       QEMU is available
-
-    2. DO NOT rely on anything which is not explicit documented
-
-    3. Errors, in special, are not documented. Applications should NOT check
-       for specific errors classes or data (it's strongly recommended to only
-       check for the "error" key)
-
-2. Regular Commands
-===================
-
-Server's responses in the examples below are always a success response, please
-refer to the QMP specification for more details on error responses.
-
-quit
-----
-
-Quit the emulator.
-
-Arguments: None.
-
-Example:
-
--> { "execute": "quit" }
-<- { "return": {} }
-
-eject
------
-
-Eject a removable medium.
-
-Arguments:
-
-- "force": force ejection (json-bool, optional)
-- "device": block device name (deprecated, use @id instead)
-            (json-string, optional)
-- "id": the name or QOM path of the guest device (json-string, optional)
-
-Example:
-
--> { "execute": "eject", "arguments": { "id": "ide0-1-0" } }
-<- { "return": {} }
-
-Note: The "force" argument defaults to false.
-
-change
-------
-
-Change a removable medium or VNC configuration.
-
-Arguments:
-
-- "device": device name (json-string)
-- "target": filename or item (json-string)
-- "arg": additional argument (json-string, optional)
-
-Examples:
-
-1. Change a removable medium
-
--> { "execute": "change",
-             "arguments": { "device": "ide1-cd0",
-                            "target": "/srv/images/Fedora-12-x86_64-DVD.iso" } }
-<- { "return": {} }
-
-2. Change VNC password
-
--> { "execute": "change",
-             "arguments": { "device": "vnc", "target": "password",
-                            "arg": "foobar1" } }
-<- { "return": {} }
-
-screendump
-----------
-
-Save screen into PPM image.
-
-Arguments:
-
-- "filename": file path (json-string)
-
-Example:
-
--> { "execute": "screendump", "arguments": { "filename": "/tmp/image" } }
-<- { "return": {} }
-
-stop
-----
-
-Stop the emulator.
-
-Arguments: None.
-
-Example:
-
--> { "execute": "stop" }
-<- { "return": {} }
-
-cont
-----
-
-Resume emulation.
-
-Arguments: None.
-
-Example:
-
--> { "execute": "cont" }
-<- { "return": {} }
-
-system_wakeup
--------------
-
-Wakeup guest from suspend.
-
-Arguments: None.
-
-Example:
-
--> { "execute": "system_wakeup" }
-<- { "return": {} }
-
-system_reset
-------------
-
-Reset the system.
-
-Arguments: None.
-
-Example:
-
--> { "execute": "system_reset" }
-<- { "return": {} }
-
-system_powerdown
-----------------
-
-Send system power down event.
-
-Arguments: None.
-
-Example:
-
--> { "execute": "system_powerdown" }
-<- { "return": {} }
-
-device_add
-----------
-
-Add a device.
-
-Arguments:
-
-- "driver": the name of the new device's driver (json-string)
-- "bus": the device's parent bus (device tree path, json-string, optional)
-- "id": the device's ID, must be unique (json-string)
-- device properties
-
-Example:
-
--> { "execute": "device_add", "arguments": { "driver": "e1000", "id": "net1" } }
-<- { "return": {} }
-
-Notes:
-
-(1) For detailed information about this command, please refer to the
-    'docs/qdev-device-use.txt' file.
-
-(2) It's possible to list device properties by running QEMU with the
-    "-device DEVICE,\?" command-line argument, where DEVICE is the device's name
-
-device_del
-----------
-
-Remove a device.
-
-Arguments:
-
-- "id": the device's ID or QOM path (json-string)
-
-Example:
-
--> { "execute": "device_del", "arguments": { "id": "net1" } }
-<- { "return": {} }
-
-Example:
-
--> { "execute": "device_del", "arguments": { "id": "/machine/peripheral-anon/device[0]" } }
-<- { "return": {} }
-
-send-key
-----------
-
-Send keys to VM.
-
-Arguments:
-
-keys array:
-    - "key": key sequence (a json-array of key union values,
-             union can be number or qcode enum)
-
-- hold-time: time to delay key up events, milliseconds. Defaults to 100
-             (json-int, optional)
-
-Example:
-
--> { "execute": "send-key",
-     "arguments": { "keys": [ { "type": "qcode", "data": "ctrl" },
-                              { "type": "qcode", "data": "alt" },
-                              { "type": "qcode", "data": "delete" } ] } }
-<- { "return": {} }
-
-cpu
----
-
-Set the default CPU.
-
-Arguments:
-
-- "index": the CPU's index (json-int)
-
-Example:
-
--> { "execute": "cpu", "arguments": { "index": 0 } }
-<- { "return": {} }
-
-Note: CPUs' indexes are obtained with the 'query-cpus' command.
-
-cpu-add
--------
-
-Adds virtual cpu
-
-Arguments:
-
-- "id": cpu id (json-int)
-
-Example:
-
--> { "execute": "cpu-add", "arguments": { "id": 2 } }
-<- { "return": {} }
-
-memsave
--------
-
-Save to disk virtual memory dump starting at 'val' of size 'size'.
-
-Arguments:
-
-- "val": the starting address (json-int)
-- "size": the memory size, in bytes (json-int)
-- "filename": file path (json-string)
-- "cpu": virtual CPU index (json-int, optional)
-
-Example:
-
--> { "execute": "memsave",
-             "arguments": { "val": 10,
-                            "size": 100,
-                            "filename": "/tmp/virtual-mem-dump" } }
-<- { "return": {} }
-
-pmemsave
---------
-
-Save to disk physical memory dump starting at 'val' of size 'size'.
-
-Arguments:
-
-- "val": the starting address (json-int)
-- "size": the memory size, in bytes (json-int)
-- "filename": file path (json-string)
-
-Example:
-
--> { "execute": "pmemsave",
-             "arguments": { "val": 10,
-                            "size": 100,
-                            "filename": "/tmp/physical-mem-dump" } }
-<- { "return": {} }
-
-inject-nmi
-----------
-
-Inject an NMI on the default CPU (x86/s390) or all CPUs (ppc64).
-
-Arguments: None.
-
-Example:
-
--> { "execute": "inject-nmi" }
-<- { "return": {} }
-
-Note: inject-nmi fails when the guest doesn't support injecting.
-
-ringbuf-write
--------------
-
-Write to a ring buffer character device.
-
-Arguments:
-
-- "device": ring buffer character device name (json-string)
-- "data": data to write (json-string)
-- "format": data format (json-string, optional)
-          - Possible values: "utf8" (default), "base64"
-
-Example:
-
--> { "execute": "ringbuf-write",
-                "arguments": { "device": "foo",
-                               "data": "abcdefgh",
-                               "format": "utf8" } }
-<- { "return": {} }
-
-ringbuf-read
--------------
-
-Read from a ring buffer character device.
-
-Arguments:
-
-- "device": ring buffer character device name (json-string)
-- "size": how many bytes to read at most (json-int)
-          - Number of data bytes, not number of characters in encoded data
-- "format": data format (json-string, optional)
-          - Possible values: "utf8" (default), "base64"
-          - Naturally, format "utf8" works only when the ring buffer
-            contains valid UTF-8 text.  Invalid UTF-8 sequences get
-            replaced.  Bug: replacement doesn't work.  Bug: can screw
-            up on encountering NUL characters, after the ring buffer
-            lost data, and when reading stops because the size limit
-            is reached.
-
-Example:
-
--> { "execute": "ringbuf-read",
-                "arguments": { "device": "foo",
-                               "size": 1000,
-                               "format": "utf8" } }
-<- {"return": "abcdefgh"}
-
-xen-save-devices-state
--------
-
-Save the state of all devices to file. The RAM and the block devices
-of the VM are not saved by this command.
-
-Arguments:
-
-- "filename": the file to save the state of the devices to as binary
-data. See xen-save-devices-state.txt for a description of the binary
-format.
-
-Example:
-
--> { "execute": "xen-save-devices-state",
-     "arguments": { "filename": "/tmp/save" } }
-<- { "return": {} }
-
-xen-load-devices-state
-----------------------
-
-Load the state of all devices from file. The RAM and the block devices
-of the VM are not loaded by this command.
-
-Arguments:
-
-- "filename": the file to load the state of the devices from as binary
-data. See xen-save-devices-state.txt for a description of the binary
-format.
-
-Example:
-
--> { "execute": "xen-load-devices-state",
-     "arguments": { "filename": "/tmp/resume" } }
-<- { "return": {} }
-
-xen-set-global-dirty-log
--------
-
-Enable or disable the global dirty log mode.
-
-Arguments:
-
-- "enable": Enable it or disable it.
-
-Example:
-
--> { "execute": "xen-set-global-dirty-log",
-     "arguments": { "enable": true } }
-<- { "return": {} }
-
-migrate
--------
-
-Migrate to URI.
-
-Arguments:
-
-- "blk": block migration, full disk copy (json-bool, optional)
-- "inc": incremental disk copy (json-bool, optional)
-- "uri": Destination URI (json-string)
-
-Example:
-
--> { "execute": "migrate", "arguments": { "uri": "tcp:0:4446" } }
-<- { "return": {} }
-
-Notes:
-
-(1) The 'query-migrate' command should be used to check migration's progress
-    and final result (this information is provided by the 'status' member)
-(2) All boolean arguments default to false
-(3) The user Monitor's "detach" argument is invalid in QMP and should not
-    be used
-
-migrate_cancel
---------------
-
-Cancel the current migration.
-
-Arguments: None.
-
-Example:
-
--> { "execute": "migrate_cancel" }
-<- { "return": {} }
-
-migrate-incoming
-----------------
-
-Continue an incoming migration
-
-Arguments:
-
-- "uri": Source/listening URI (json-string)
-
-Example:
-
--> { "execute": "migrate-incoming", "arguments": { "uri": "tcp::4446" } }
-<- { "return": {} }
-
-Notes:
-
-(1) QEMU must be started with -incoming defer to allow migrate-incoming to
-    be used
-(2) The uri format is the same as for -incoming
-
-migrate-set-cache-size
-----------------------
-
-Set cache size to be used by XBZRLE migration, the cache size will be rounded
-down to the nearest power of 2
-
-Arguments:
-
-- "value": cache size in bytes (json-int)
-
-Example:
-
--> { "execute": "migrate-set-cache-size", "arguments": { "value": 536870912 } }
-<- { "return": {} }
-
-migrate-start-postcopy
-----------------------
-
-Switch an in-progress migration to postcopy mode. Ignored after the end of
-migration (or once already in postcopy).
-
-Example:
--> { "execute": "migrate-start-postcopy" }
-<- { "return": {} }
-
-query-migrate-cache-size
-------------------------
-
-Show cache size to be used by XBZRLE migration
-
-returns a json-object with the following information:
-- "size" : json-int
-
-Example:
-
--> { "execute": "query-migrate-cache-size" }
-<- { "return": 67108864 }
-
-migrate_set_speed
------------------
-
-Set maximum speed for migrations.
-
-Arguments:
-
-- "value": maximum speed, in bytes per second (json-int)
-
-Example:
-
--> { "execute": "migrate_set_speed", "arguments": { "value": 1024 } }
-<- { "return": {} }
-
-migrate_set_downtime
---------------------
-
-Set maximum tolerated downtime (in seconds) for migrations.
-
-Arguments:
-
-- "value": maximum downtime (json-number)
-
-Example:
-
--> { "execute": "migrate_set_downtime", "arguments": { "value": 0.1 } }
-<- { "return": {} }
-
-x-colo-lost-heartbeat
---------------------
-
-Tell COLO that heartbeat is lost, a failover or takeover is needed.
-
-Example:
-
--> { "execute": "x-colo-lost-heartbeat" }
-<- { "return": {} }
-
-client_migrate_info
--------------------
-
-Set migration information for remote display.  This makes the server
-ask the client to automatically reconnect using the new parameters
-once migration finished successfully.  Only implemented for SPICE.
-
-Arguments:
-
-- "protocol":     must be "spice" (json-string)
-- "hostname":     migration target hostname (json-string)
-- "port":         spice tcp port for plaintext channels (json-int, optional)
-- "tls-port":     spice tcp port for tls-secured channels (json-int, optional)
-- "cert-subject": server certificate subject (json-string, optional)
-
-Example:
-
--> { "execute": "client_migrate_info",
-     "arguments": { "protocol": "spice",
-                    "hostname": "virt42.lab.kraxel.org",
-                    "port": 1234 } }
-<- { "return": {} }
-
-dump
-
-
-Dump guest memory to file. The file can be processed with crash or gdb.
-
-Arguments:
-
-- "paging": do paging to get guest's memory mapping (json-bool)
-- "protocol": destination file(started with "file:") or destination file
-              descriptor (started with "fd:") (json-string)
-- "detach": if specified, command will return immediately, without waiting
-            for the dump to finish. The user can track progress using
-            "query-dump". (json-bool)
-- "begin": the starting physical address. It's optional, and should be specified
-           with length together (json-int)
-- "length": the memory size, in bytes. It's optional, and should be specified
-            with begin together (json-int)
-- "format": the format of guest memory dump. It's optional, and can be
-            elf|kdump-zlib|kdump-lzo|kdump-snappy, but non-elf formats will
-            conflict with paging and filter, ie. begin and length (json-string)
-
-Example:
-
--> { "execute": "dump-guest-memory", "arguments": { "protocol": "fd:dump" } }
-<- { "return": {} }
-
-Notes:
-
-(1) All boolean arguments default to false
-
-query-dump-guest-memory-capability
-----------
-
-Show available formats for 'dump-guest-memory'
-
-Example:
-
--> { "execute": "query-dump-guest-memory-capability" }
-<- { "return": { "formats":
-                    ["elf", "kdump-zlib", "kdump-lzo", "kdump-snappy"] }
-
-query-dump
-----------
-
-Query background dump status.
-
-Arguments: None.
-
-Example:
-
--> { "execute": "query-dump" }
-<- { "return": { "status": "active", "completed": 1024000,
-                 "total": 2048000 } }
-
-dump-skeys
-----------
-
-Save guest storage keys to file.
-
-Arguments:
-
-- "filename": file path (json-string)
-
-Example:
-
--> { "execute": "dump-skeys", "arguments": { "filename": "/tmp/skeys" } }
-<- { "return": {} }
-
-netdev_add
-----------
-
-Add host network device.
-
-Arguments:
-
-- "type": the device type, "tap", "user", ... (json-string)
-- "id": the device's ID, must be unique (json-string)
-- device options
-
-Example:
-
--> { "execute": "netdev_add",
-     "arguments": { "type": "user", "id": "netdev1",
-                    "dnssearch": "example.org" } }
-<- { "return": {} }
-
-Note: The supported device options are the same ones supported by the '-netdev'
-      command-line argument, which are listed in the '-help' output or QEMU's
-      manual
-
-netdev_del
-----------
-
-Remove host network device.
-
-Arguments:
-
-- "id": the device's ID, must be unique (json-string)
-
-Example:
-
--> { "execute": "netdev_del", "arguments": { "id": "netdev1" } }
-<- { "return": {} }
-
-
-object-add
-----------
-
-Create QOM object.
-
-Arguments:
-
-- "qom-type": the object's QOM type, i.e. the class name (json-string)
-- "id": the object's ID, must be unique (json-string)
-- "props": a dictionary of object property values (optional, json-dict)
-
-Example:
-
--> { "execute": "object-add", "arguments": { "qom-type": "rng-random", "id": "rng1",
-     "props": { "filename": "/dev/hwrng" } } }
-<- { "return": {} }
-
-object-del
-----------
-
-Remove QOM object.
-
-Arguments:
-
-- "id": the object's ID (json-string)
-
-Example:
-
--> { "execute": "object-del", "arguments": { "id": "rng1" } }
-<- { "return": {} }
-
-
-block_resize
-------------
-
-Resize a block image while a guest is running.
-
-Arguments:
-
-- "device": the device's ID, must be unique (json-string)
-- "node-name": the node name in the block driver state graph (json-string)
-- "size": new size
-
-Example:
-
--> { "execute": "block_resize", "arguments": { "device": "scratch", "size": 1073741824 } }
-<- { "return": {} }
-
-block-stream
-------------
-
-Copy data from a backing file into a block device.
-
-Arguments:
-
-- "job-id": Identifier for the newly-created block job. If omitted,
-            the device name will be used. (json-string, optional)
-- "device": The device name or node-name of a root node (json-string)
-- "base": The file name of the backing image above which copying starts.
-          It cannot be set if 'base-node' is also set (json-string, optional)
-- "base-node": the node name of the backing image above which copying starts.
-               It cannot be set if 'base' is also set.
-               (json-string, optional) (Since 2.8)
-- "backing-file": The backing file string to write into the active layer. This
-                  filename is not validated.
-
-                  If a pathname string is such that it cannot be resolved by
-                  QEMU, that means that subsequent QMP or HMP commands must use
-                  node-names for the image in question, as filename lookup
-                  methods will fail.
-
-                  If not specified, QEMU will automatically determine the
-                  backing file string to use, or error out if there is no
-                  obvious choice.  Care should be taken when specifying the
-                  string, to specify a valid filename or protocol.
-                  (json-string, optional) (Since 2.1)
-- "speed":  the maximum speed, in bytes per second (json-int, optional)
-- "on-error": the action to take on an error (default 'report').  'stop' and
-              'enospc' can only be used if the block device supports io-status.
-              (json-string, optional) (Since 2.1)
-
-Example:
-
--> { "execute": "block-stream", "arguments": { "device": "virtio0",
-                                               "base": "/tmp/master.qcow2" } }
-<- { "return": {} }
-
-block-commit
-------------
-
-Live commit of data from overlay image nodes into backing nodes - i.e., writes
-data between 'top' and 'base' into 'base'.
-
-Arguments:
-
-- "job-id": Identifier for the newly-created block job. If omitted,
-            the device name will be used. (json-string, optional)
-- "device": The device name or node-name of a root node (json-string)
-- "base": The file name of the backing image to write data into.
-          If not specified, this is the deepest backing image
-          (json-string, optional)
-- "top":  The file name of the backing image within the image chain,
-          which contains the topmost data to be committed down. If
-          not specified, this is the active layer. (json-string, optional)
-
-- backing-file:     The backing file string to write into the overlay
-                    image of 'top'.  If 'top' is the active layer,
-                    specifying a backing file string is an error. This
-                    filename is not validated.
-
-                    If a pathname string is such that it cannot be
-                    resolved by QEMU, that means that subsequent QMP or
-                    HMP commands must use node-names for the image in
-                    question, as filename lookup methods will fail.
-
-                    If not specified, QEMU will automatically determine
-                    the backing file string to use, or error out if
-                    there is no obvious choice. Care should be taken
-                    when specifying the string, to specify a valid
-                    filename or protocol.
-                    (json-string, optional) (Since 2.1)
-
-          If top == base, that is an error.
-          If top == active, the job will not be completed by itself,
-          user needs to complete the job with the block-job-complete
-          command after getting the ready event. (Since 2.0)
-
-          If the base image is smaller than top, then the base image
-          will be resized to be the same size as top.  If top is
-          smaller than the base image, the base will not be
-          truncated.  If you want the base image size to match the
-          size of the smaller top, you can safely truncate it
-          yourself once the commit operation successfully completes.
-          (json-string)
-- "speed":  the maximum speed, in bytes per second (json-int, optional)
-
-
-Example:
-
--> { "execute": "block-commit", "arguments": { "device": "virtio0",
-                                              "top": "/tmp/snap1.qcow2" } }
-<- { "return": {} }
-
-drive-backup
-------------
-
-Start a point-in-time copy of a block device to a new destination.  The
-status of ongoing drive-backup operations can be checked with
-query-block-jobs where the BlockJobInfo.type field has the value 'backup'.
-The operation can be stopped before it has completed using the
-block-job-cancel command.
-
-Arguments:
-
-- "job-id": Identifier for the newly-created block job. If omitted,
-            the device name will be used. (json-string, optional)
-- "device": the device name or node-name of a root node which should be copied.
-            (json-string)
-- "target": the target of the new image. If the file exists, or if it is a
-            device, the existing file/device will be used as the new
-            destination.  If it does not exist, a new file will be created.
-            (json-string)
-- "format": the format of the new destination, default is to probe if 'mode' is
-            'existing', else the format of the source
-            (json-string, optional)
-- "sync": what parts of the disk image should be copied to the destination;
-  possibilities include "full" for all the disk, "top" for only the sectors
-  allocated in the topmost image, "incremental" for only the dirty sectors in
-  the bitmap, or "none" to only replicate new I/O (MirrorSyncMode).
-- "bitmap": dirty bitmap name for sync==incremental. Must be present if sync
-            is "incremental", must NOT be present otherwise.
-- "mode": whether and how QEMU should create a new image
-          (NewImageMode, optional, default 'absolute-paths')
-- "speed": the maximum speed, in bytes per second (json-int, optional)
-- "compress": true to compress data, if the target format supports it.
-              (json-bool, optional, default false)
-- "on-source-error": the action to take on an error on the source, default
-                     'report'.  'stop' and 'enospc' can only be used
-                     if the block device supports io-status.
-                     (BlockdevOnError, optional)
-- "on-target-error": the action to take on an error on the target, default
-                     'report' (no limitations, since this applies to
-                     a different block device than device).
-                     (BlockdevOnError, optional)
-
-Example:
--> { "execute": "drive-backup", "arguments": { "device": "drive0",
-                                               "sync": "full",
-                                               "target": "backup.img" } }
-<- { "return": {} }
-
-blockdev-backup
----------------
-
-The device version of drive-backup: this command takes an existing named device
-as backup target.
-
-Arguments:
-
-- "job-id": Identifier for the newly-created block job. If omitted,
-            the device name will be used. (json-string, optional)
-- "device": the device name or node-name of a root node which should be copied.
-            (json-string)
-- "target": the name of the backup target device. (json-string)
-- "sync": what parts of the disk image should be copied to the destination;
-          possibilities include "full" for all the disk, "top" for only the
-          sectors allocated in the topmost image, or "none" to only replicate
-          new I/O (MirrorSyncMode).
-- "speed": the maximum speed, in bytes per second (json-int, optional)
-- "compress": true to compress data, if the target format supports it.
-              (json-bool, optional, default false)
-- "on-source-error": the action to take on an error on the source, default
-                     'report'.  'stop' and 'enospc' can only be used
-                     if the block device supports io-status.
-                     (BlockdevOnError, optional)
-- "on-target-error": the action to take on an error on the target, default
-                     'report' (no limitations, since this applies to
-                     a different block device than device).
-                     (BlockdevOnError, optional)
-
-Example:
--> { "execute": "blockdev-backup", "arguments": { "device": "src-id",
-                                                  "sync": "full",
-                                                  "target": "tgt-id" } }
-<- { "return": {} }
-
-transaction
------------
-
-Atomically operate on one or more block devices.  Operations that are
-currently supported:
-
-    - drive-backup
-    - blockdev-backup
-    - blockdev-snapshot-sync
-    - blockdev-snapshot-internal-sync
-    - abort
-    - block-dirty-bitmap-add
-    - block-dirty-bitmap-clear
-
-Refer to the qemu/qapi-schema.json file for minimum required QEMU
-versions for these operations.  A list of dictionaries is accepted,
-that contains the actions to be performed.  If there is any failure
-performing any of the operations, all operations for the group are
-abandoned.
-
-For external snapshots, the dictionary contains the device, the file to use for
-the new snapshot, and the format.  The default format, if not specified, is
-qcow2.
-
-Each new snapshot defaults to being created by QEMU (wiping any
-contents if the file already exists), but it is also possible to reuse
-an externally-created file.  In the latter case, you should ensure that
-the new image file has the same contents as the current one; QEMU cannot
-perform any meaningful check.  Typically this is achieved by using the
-current image file as the backing file for the new image.
-
-On failure, the original disks pre-snapshot attempt will be used.
-
-For internal snapshots, the dictionary contains the device and the snapshot's
-name.  If an internal snapshot matching name already exists, the request will
-be rejected.  Only some image formats support it, for example, qcow2, rbd,
-and sheepdog.
-
-On failure, qemu will try delete the newly created internal snapshot in the
-transaction.  When an I/O error occurs during deletion, the user needs to fix
-it later with qemu-img or other command.
-
-Arguments:
-
-actions array:
-    - "type": the operation to perform (json-string).  Possible
-              values: "drive-backup", "blockdev-backup",
-                      "blockdev-snapshot-sync",
-                      "blockdev-snapshot-internal-sync",
-                      "abort", "block-dirty-bitmap-add",
-                      "block-dirty-bitmap-clear"
-    - "data": a dictionary.  The contents depend on the value
-      of "type".  When "type" is "blockdev-snapshot-sync":
-      - "device": device name to snapshot (json-string)
-      - "node-name": graph node name to snapshot (json-string)
-      - "snapshot-file": name of new image file (json-string)
-      - "snapshot-node-name": graph node name of the new snapshot (json-string)
-      - "format": format of new image (json-string, optional)
-      - "mode": whether and how QEMU should create the snapshot file
-        (NewImageMode, optional, default "absolute-paths")
-      When "type" is "blockdev-snapshot-internal-sync":
-      - "device": the device name or node-name of a root node to snapshot
-                  (json-string)
-      - "name": name of the new snapshot (json-string)
-
-Example:
-
--> { "execute": "transaction",
-     "arguments": { "actions": [
-         { "type": "blockdev-snapshot-sync", "data" : { "device": "ide-hd0",
-                                         "snapshot-file": "/some/place/my-image",
-                                         "format": "qcow2" } },
-         { "type": "blockdev-snapshot-sync", "data" : { "node-name": "myfile",
-                                         "snapshot-file": "/some/place/my-image2",
-                                         "snapshot-node-name": "node3432",
-                                         "mode": "existing",
-                                         "format": "qcow2" } },
-         { "type": "blockdev-snapshot-sync", "data" : { "device": "ide-hd1",
-                                         "snapshot-file": "/some/place/my-image2",
-                                         "mode": "existing",
-                                         "format": "qcow2" } },
-         { "type": "blockdev-snapshot-internal-sync", "data" : {
-                                         "device": "ide-hd2",
-                                         "name": "snapshot0" } } ] } }
-<- { "return": {} }
-
-block-dirty-bitmap-add
-----------------------
-Since 2.4
-
-Create a dirty bitmap with a name on the device, and start tracking the writes.
-
-Arguments:
-
-- "node": device/node on which to create dirty bitmap (json-string)
-- "name": name of the new dirty bitmap (json-string)
-- "granularity": granularity to track writes with (int, optional)
-
-Example:
-
--> { "execute": "block-dirty-bitmap-add", "arguments": { "node": "drive0",
-                                                   "name": "bitmap0" } }
-<- { "return": {} }
-
-block-dirty-bitmap-remove
--------------------------
-Since 2.4
-
-Stop write tracking and remove the dirty bitmap that was created with
-block-dirty-bitmap-add.
-
-Arguments:
-
-- "node": device/node on which to remove dirty bitmap (json-string)
-- "name": name of the dirty bitmap to remove (json-string)
-
-Example:
-
--> { "execute": "block-dirty-bitmap-remove", "arguments": { "node": "drive0",
-                                                      "name": "bitmap0" } }
-<- { "return": {} }
-
-block-dirty-bitmap-clear
-------------------------
-Since 2.4
-
-Reset the dirty bitmap associated with a node so that an incremental backup
-from this point in time forward will only backup clusters modified after this
-clear operation.
-
-Arguments:
-
-- "node": device/node on which to remove dirty bitmap (json-string)
-- "name": name of the dirty bitmap to remove (json-string)
-
-Example:
-
--> { "execute": "block-dirty-bitmap-clear", "arguments": { "node": "drive0",
-                                                           "name": "bitmap0" } }
-<- { "return": {} }
-
-blockdev-snapshot-sync
-----------------------
-
-Synchronous snapshot of a block device. snapshot-file specifies the
-target of the new image. If the file exists, or if it is a device, the
-snapshot will be created in the existing file/device. If does not
-exist, a new file will be created. format specifies the format of the
-snapshot image, default is qcow2.
-
-Arguments:
-
-- "device": device name to snapshot (json-string)
-- "node-name": graph node name to snapshot (json-string)
-- "snapshot-file": name of new image file (json-string)
-- "snapshot-node-name": graph node name of the new snapshot (json-string)
-- "mode": whether and how QEMU should create the snapshot file
-  (NewImageMode, optional, default "absolute-paths")
-- "format": format of new image (json-string, optional)
-
-Example:
-
--> { "execute": "blockdev-snapshot-sync", "arguments": { "device": "ide-hd0",
-                                                         "snapshot-file":
-                                                        "/some/place/my-image",
-                                                        "format": "qcow2" } }
-<- { "return": {} }
-
-blockdev-snapshot
------------------
-Since 2.5
-
-Create a snapshot, by installing 'node' as the backing image of
-'overlay'. Additionally, if 'node' is associated with a block
-device, the block device changes to using 'overlay' as its new active
-image.
-
-Arguments:
-
-- "node": device that will have a snapshot created (json-string)
-- "overlay": device that will have 'node' as its backing image (json-string)
-
-Example:
-
--> { "execute": "blockdev-add",
-                "arguments": { "driver": "qcow2",
-                               "node-name": "node1534",
-                               "file": { "driver": "file",
-                                         "filename": "hd1.qcow2" },
-                               "backing": "" } }
-
-<- { "return": {} }
-
--> { "execute": "blockdev-snapshot", "arguments": { "node": "ide-hd0",
-                                                    "overlay": "node1534" } }
-<- { "return": {} }
-
-blockdev-snapshot-internal-sync
--------------------------------
-
-Synchronously take an internal snapshot of a block device when the format of
-image used supports it.  If the name is an empty string, or a snapshot with
-name already exists, the operation will fail.
-
-Arguments:
-
-- "device": the device name or node-name of a root node to snapshot
-            (json-string)
-- "name": name of the new snapshot (json-string)
-
-Example:
-
--> { "execute": "blockdev-snapshot-internal-sync",
-                "arguments": { "device": "ide-hd0",
-                               "name": "snapshot0" }
-   }
-<- { "return": {} }
-
-blockdev-snapshot-delete-internal-sync
---------------------------------------
-
-Synchronously delete an internal snapshot of a block device when the format of
-image used supports it.  The snapshot is identified by name or id or both.  One
-of name or id is required.  If the snapshot is not found, the operation will
-fail.
-
-Arguments:
-
-- "device": the device name or node-name of a root node (json-string)
-- "id": ID of the snapshot (json-string, optional)
-- "name": name of the snapshot (json-string, optional)
-
-Example:
-
--> { "execute": "blockdev-snapshot-delete-internal-sync",
-                "arguments": { "device": "ide-hd0",
-                               "name": "snapshot0" }
-   }
-<- { "return": {
-                   "id": "1",
-                   "name": "snapshot0",
-                   "vm-state-size": 0,
-                   "date-sec": 1000012,
-                   "date-nsec": 10,
-                   "vm-clock-sec": 100,
-                   "vm-clock-nsec": 20
-     }
-   }
-
-drive-mirror
-------------
-
-Start mirroring a block device's writes to a new destination. target
-specifies the target of the new image. If the file exists, or if it is
-a device, it will be used as the new destination for writes. If it does not
-exist, a new file will be created. format specifies the format of the
-mirror image, default is to probe if mode='existing', else the format
-of the source.
-
-Arguments:
-
-- "job-id": Identifier for the newly-created block job. If omitted,
-            the device name will be used. (json-string, optional)
-- "device": the device name or node-name of a root node whose writes should be
-            mirrored. (json-string)
-- "target": name of new image file (json-string)
-- "format": format of new image (json-string, optional)
-- "node-name": the name of the new block driver state in the node graph
-               (json-string, optional)
-- "replaces": the block driver node name to replace when finished
-              (json-string, optional)
-- "mode": how an image file should be created into the target
-  file/device (NewImageMode, optional, default 'absolute-paths')
-- "speed": maximum speed of the streaming job, in bytes per second
-  (json-int)
-- "granularity": granularity of the dirty bitmap, in bytes (json-int, optional)
-- "buf-size": maximum amount of data in flight from source to target, in bytes
-  (json-int, default 10M)
-- "sync": what parts of the disk image should be copied to the destination;
-  possibilities include "full" for all the disk, "top" for only the sectors
-  allocated in the topmost image, or "none" to only replicate new I/O
-  (MirrorSyncMode).
-- "on-source-error": the action to take on an error on the source
-  (BlockdevOnError, default 'report')
-- "on-target-error": the action to take on an error on the target
-  (BlockdevOnError, default 'report')
-- "unmap": whether the target sectors should be discarded where source has only
-  zeroes. (json-bool, optional, default true)
-
-The default value of the granularity is the image cluster size clamped
-between 4096 and 65536, if the image format defines one.  If the format
-does not define a cluster size, the default value of the granularity
-is 65536.
-
-
-Example:
-
--> { "execute": "drive-mirror", "arguments": { "device": "ide-hd0",
-                                               "target": "/some/place/my-image",
-                                               "sync": "full",
-                                               "format": "qcow2" } }
-<- { "return": {} }
-
-blockdev-mirror
-------------
-
-Start mirroring a block device's writes to another block device. target
-specifies the target of mirror operation.
-
-Arguments:
-
-- "job-id": Identifier for the newly-created block job. If omitted,
-            the device name will be used. (json-string, optional)
-- "device": The device name or node-name of a root node whose writes should be
-            mirrored (json-string)
-- "target": device name to mirror to (json-string)
-- "replaces": the block driver node name to replace when finished
-              (json-string, optional)
-- "speed": maximum speed of the streaming job, in bytes per second
-  (json-int)
-- "granularity": granularity of the dirty bitmap, in bytes (json-int, optional)
-- "buf_size": maximum amount of data in flight from source to target, in bytes
-  (json-int, default 10M)
-- "sync": what parts of the disk image should be copied to the destination;
-  possibilities include "full" for all the disk, "top" for only the sectors
-  allocated in the topmost image, or "none" to only replicate new I/O
-  (MirrorSyncMode).
-- "on-source-error": the action to take on an error on the source
-  (BlockdevOnError, default 'report')
-- "on-target-error": the action to take on an error on the target
-  (BlockdevOnError, default 'report')
-
-The default value of the granularity is the image cluster size clamped
-between 4096 and 65536, if the image format defines one.  If the format
-does not define a cluster size, the default value of the granularity
-is 65536.
-
-Example:
-
--> { "execute": "blockdev-mirror", "arguments": { "device": "ide-hd0",
-                                                  "target": "target0",
-                                                  "sync": "full" } }
-<- { "return": {} }
-
-change-backing-file
--------------------
-Since: 2.1
-
-Change the backing file in the image file metadata.  This does not cause
-QEMU to reopen the image file to reparse the backing filename (it may,
-however, perform a reopen to change permissions from r/o -> r/w -> r/o,
-if needed). The new backing file string is written into the image file
-metadata, and the QEMU internal strings are updated.
-
-Arguments:
-
-- "image-node-name":    The name of the block driver state node of the
-                        image to modify.  The "device" is argument is used to
-                        verify "image-node-name" is in the chain described by
-                        "device".
-                        (json-string, optional)
-
-- "device":             The device name or node-name of the root node that owns
-                        image-node-name.
-                        (json-string)
-
-- "backing-file":       The string to write as the backing file.  This string is
-                        not validated, so care should be taken when specifying
-                        the string or the image chain may not be able to be
-                        reopened again.
-                        (json-string)
-
-Returns: Nothing on success
-         If "device" does not exist or cannot be determined, DeviceNotFound
-
-balloon
--------
-
-Request VM to change its memory allocation (in bytes).
-
-Arguments:
-
-- "value": New memory allocation (json-int)
-
-Example:
-
--> { "execute": "balloon", "arguments": { "value": 536870912 } }
-<- { "return": {} }
-
-set_link
---------
-
-Change the link status of a network adapter.
-
-Arguments:
-
-- "name": network device name (json-string)
-- "up": status is up (json-bool)
-
-Example:
-
--> { "execute": "set_link", "arguments": { "name": "e1000.0", "up": false } }
-<- { "return": {} }
-
-getfd
------
-
-Receive a file descriptor via SCM rights and assign it a name.
-
-Arguments:
-
-- "fdname": file descriptor name (json-string)
-
-Example:
-
--> { "execute": "getfd", "arguments": { "fdname": "fd1" } }
-<- { "return": {} }
-
-Notes:
-
-(1) If the name specified by the "fdname" argument already exists,
-    the file descriptor assigned to it will be closed and replaced
-    by the received file descriptor.
-(2) The 'closefd' command can be used to explicitly close the file
-    descriptor when it is no longer needed.
-
-closefd
--------
-
-Close a file descriptor previously passed via SCM rights.
-
-Arguments:
-
-- "fdname": file descriptor name (json-string)
-
-Example:
-
--> { "execute": "closefd", "arguments": { "fdname": "fd1" } }
-<- { "return": {} }
-
-add-fd
--------
-
-Add a file descriptor, that was passed via SCM rights, to an fd set.
-
-Arguments:
-
-- "fdset-id": The ID of the fd set to add the file descriptor to.
-              (json-int, optional)
-- "opaque": A free-form string that can be used to describe the fd.
-            (json-string, optional)
-
-Return a json-object with the following information:
-
-- "fdset-id": The ID of the fd set that the fd was added to. (json-int)
-- "fd": The file descriptor that was received via SCM rights and added to the
-        fd set. (json-int)
-
-Example:
-
--> { "execute": "add-fd", "arguments": { "fdset-id": 1 } }
-<- { "return": { "fdset-id": 1, "fd": 3 } }
-
-Notes:
-
-(1) The list of fd sets is shared by all monitor connections.
-(2) If "fdset-id" is not specified, a new fd set will be created.
-
-remove-fd
----------
-
-Remove a file descriptor from an fd set.
-
-Arguments:
-
-- "fdset-id": The ID of the fd set that the file descriptor belongs to.
-              (json-int)
-- "fd": The file descriptor that is to be removed. (json-int, optional)
-
-Example:
-
--> { "execute": "remove-fd", "arguments": { "fdset-id": 1, "fd": 3 } }
-<- { "return": {} }
-
-Notes:
-
-(1) The list of fd sets is shared by all monitor connections.
-(2) If "fd" is not specified, all file descriptors in "fdset-id" will be
-    removed.
-
-query-fdsets
--------------
-
-Return information describing all fd sets.
-
-Arguments: None
-
-Example:
-
--> { "execute": "query-fdsets" }
-<- { "return": [
-       {
-         "fds": [
-           {
-             "fd": 30,
-             "opaque": "rdonly:/path/to/file"
-           },
-           {
-             "fd": 24,
-             "opaque": "rdwr:/path/to/file"
-           }
-         ],
-         "fdset-id": 1
-       },
-       {
-         "fds": [
-           {
-             "fd": 28
-           },
-           {
-             "fd": 29
-           }
-         ],
-         "fdset-id": 0
-       }
-     ]
-   }
-
-Note: The list of fd sets is shared by all monitor connections.
-
-block_passwd
-------------
-
-Set the password of encrypted block devices.
-
-Arguments:
-
-- "device": device name (json-string)
-- "node-name": name in the block driver state graph (json-string)
-- "password": password (json-string)
-
-Example:
-
--> { "execute": "block_passwd", "arguments": { "device": "ide0-hd0",
-                                               "password": "12345" } }
-<- { "return": {} }
-
-block_set_io_throttle
-------------
-
-Change I/O throttle limits for a block drive.
-
-Arguments:
-
-- "device": block device name (deprecated, use @id instead)
-            (json-string, optional)
-- "id": the name or QOM path of the guest device (json-string, optional)
-- "bps": total throughput limit in bytes per second (json-int)
-- "bps_rd": read throughput limit in bytes per second (json-int)
-- "bps_wr": write throughput limit in bytes per second (json-int)
-- "iops": total I/O operations per second (json-int)
-- "iops_rd": read I/O operations per second (json-int)
-- "iops_wr": write I/O operations per second (json-int)
-- "bps_max": total throughput limit during bursts, in bytes (json-int, optional)
-- "bps_rd_max": read throughput limit during bursts, in bytes (json-int, optional)
-- "bps_wr_max": write throughput limit during bursts, in bytes (json-int, optional)
-- "iops_max": total I/O operations per second during bursts (json-int, optional)
-- "iops_rd_max": read I/O operations per second during bursts (json-int, optional)
-- "iops_wr_max": write I/O operations per second during bursts (json-int, optional)
-- "bps_max_length": maximum length of the @bps_max burst period, in seconds (json-int, optional)
-- "bps_rd_max_length": maximum length of the @bps_rd_max burst period, in seconds (json-int, optional)
-- "bps_wr_max_length": maximum length of the @bps_wr_max burst period, in seconds (json-int, optional)
-- "iops_max_length": maximum length of the @iops_max burst period, in seconds (json-int, optional)
-- "iops_rd_max_length": maximum length of the @iops_rd_max burst period, in seconds (json-int, optional)
-- "iops_wr_max_length": maximum length of the @iops_wr_max burst period, in seconds (json-int, optional)
-- "iops_size":  I/O size in bytes when limiting (json-int, optional)
-- "group": throttle group name (json-string, optional)
-
-Example:
-
--> { "execute": "block_set_io_throttle", "arguments": { "id": "ide0-1-0",
-                                               "bps": 1000000,
-                                               "bps_rd": 0,
-                                               "bps_wr": 0,
-                                               "iops": 0,
-                                               "iops_rd": 0,
-                                               "iops_wr": 0,
-                                               "bps_max": 8000000,
-                                               "bps_rd_max": 0,
-                                               "bps_wr_max": 0,
-                                               "iops_max": 0,
-                                               "iops_rd_max": 0,
-                                               "iops_wr_max": 0,
-                                               "bps_max_length": 60,
-                                               "iops_size": 0 } }
-<- { "return": {} }
-
-set_password
-------------
-
-Set the password for vnc/spice protocols.
-
-Arguments:
-
-- "protocol": protocol name (json-string)
-- "password": password (json-string)
-- "connected": [ keep | disconnect | fail ] (json-string, optional)
-
-Example:
-
--> { "execute": "set_password", "arguments": { "protocol": "vnc",
-                                               "password": "secret" } }
-<- { "return": {} }
-
-expire_password
----------------
-
-Set the password expire time for vnc/spice protocols.
-
-Arguments:
-
-- "protocol": protocol name (json-string)
-- "time": [ now | never | +secs | secs ] (json-string)
-
-Example:
-
--> { "execute": "expire_password", "arguments": { "protocol": "vnc",
-                                                  "time": "+60" } }
-<- { "return": {} }
-
-add_client
-----------
-
-Add a graphics client
-
-Arguments:
-
-- "protocol": protocol name (json-string)
-- "fdname": file descriptor name (json-string)
-- "skipauth": whether to skip authentication (json-bool, optional)
-- "tls": whether to perform TLS (json-bool, optional)
-
-Example:
-
--> { "execute": "add_client", "arguments": { "protocol": "vnc",
-                                             "fdname": "myclient" } }
-<- { "return": {} }
-
-qmp_capabilities
-----------------
-
-Enable QMP capabilities.
-
-Arguments: None.
-
-Example:
-
--> { "execute": "qmp_capabilities" }
-<- { "return": {} }
-
-Note: This command must be issued before issuing any other command.
-
-human-monitor-command
----------------------
-
-Execute a Human Monitor command.
-
-Arguments:
-
-- command-line: the command name and its arguments, just like the
-                Human Monitor's shell (json-string)
-- cpu-index: select the CPU number to be used by commands which access CPU
-             data, like 'info registers'. The Monitor selects CPU 0 if this
-             argument is not provided (json-int, optional)
-
-Example:
-
--> { "execute": "human-monitor-command", "arguments": { "command-line": "info kvm" } }
-<- { "return": "kvm support: enabled\r\n" }
-
-Notes:
-
-(1) The Human Monitor is NOT an stable interface, this means that command
-    names, arguments and responses can change or be removed at ANY time.
-    Applications that rely on long term stability guarantees should NOT
-    use this command
-
-(2) Limitations:
-
-    o This command is stateless, this means that commands that depend
-      on state information (such as getfd) might not work
-
-    o Commands that prompt the user for data (eg. 'cont' when the block
-      device is encrypted) don't currently work
-
-3. Query Commands
-=================
-
-
-query-version
--------------
-
-Show QEMU version.
-
-Return a json-object with the following information:
-
-- "qemu": A json-object containing three integer values:
-    - "major": QEMU's major version (json-int)
-    - "minor": QEMU's minor version (json-int)
-    - "micro": QEMU's micro version (json-int)
-- "package": package's version (json-string)
-
-Example:
-
--> { "execute": "query-version" }
-<- {
-      "return":{
-         "qemu":{
-            "major":0,
-            "minor":11,
-            "micro":5
-         },
-         "package":""
-      }
-   }
-
-query-commands
---------------
-
-List QMP available commands.
-
-Each command is represented by a json-object, the returned value is a json-array
-of all commands.
-
-Each json-object contain:
-
-- "name": command's name (json-string)
-
-Example:
-
--> { "execute": "query-commands" }
-<- {
-      "return":[
-         {
-            "name":"query-balloon"
-         },
-         {
-            "name":"system_powerdown"
-         }
-      ]
-   }
-
-Note: This example has been shortened as the real response is too long.
-
-query-events
---------------
-
-List QMP available events.
-
-Each event is represented by a json-object, the returned value is a json-array
-of all events.
-
-Each json-object contains:
-
-- "name": event's name (json-string)
-
-Example:
-
--> { "execute": "query-events" }
-<- {
-      "return":[
-         {
-            "name":"SHUTDOWN"
-         },
-         {
-            "name":"RESET"
-         }
-      ]
-   }
-
-Note: This example has been shortened as the real response is too long.
-
-query-qmp-schema
-----------------
-
-Return the QMP wire schema.  The returned value is a json-array of
-named schema entities.  Entities are commands, events and various
-types.  See docs/qapi-code-gen.txt for information on their structure
-and intended use.
-
-query-chardev
--------------
-
-Each device is represented by a json-object. The returned value is a json-array
-of all devices.
-
-Each json-object contain the following:
-
-- "label": device's label (json-string)
-- "filename": device's file (json-string)
-- "frontend-open": open/closed state of the frontend device attached to this
-                   backend (json-bool)
-
-Example:
-
--> { "execute": "query-chardev" }
-<- {
-      "return": [
-         {
-            "label": "charchannel0",
-            "filename": "unix:/var/lib/libvirt/qemu/seabios.rhel6.agent,server",
-            "frontend-open": false
-         },
-         {
-            "label": "charmonitor",
-            "filename": "unix:/var/lib/libvirt/qemu/seabios.rhel6.monitor,server",
-            "frontend-open": true
-         },
-         {
-            "label": "charserial0",
-            "filename": "pty:/dev/pts/2",
-            "frontend-open": true
-         }
-      ]
-   }
-
-query-chardev-backends
--------------
-
-List available character device backends.
-
-Each backend is represented by a json-object, the returned value is a json-array
-of all backends.
-
-Each json-object contains:
-
-- "name": backend name (json-string)
-
-Example:
-
--> { "execute": "query-chardev-backends" }
-<- {
-      "return":[
-         {
-            "name":"udp"
-         },
-         {
-            "name":"tcp"
-         },
-         {
-            "name":"unix"
-         },
-         {
-            "name":"spiceport"
-         }
-      ]
-   }
-
-query-block
------------
-
-Show the block devices.
-
-Each block device information is stored in a json-object and the returned value
-is a json-array of all devices.
-
-Each json-object contain the following:
-
-- "device": device name (json-string)
-- "type": device type (json-string)
-         - deprecated, retained for backward compatibility
-         - Possible values: "unknown"
-- "removable": true if the device is removable, false otherwise (json-bool)
-- "locked": true if the device is locked, false otherwise (json-bool)
-- "tray_open": only present if removable, true if the device has a tray,
-               and it is open (json-bool)
-- "inserted": only present if the device is inserted, it is a json-object
-   containing the following:
-         - "file": device file name (json-string)
-         - "ro": true if read-only, false otherwise (json-bool)
-         - "drv": driver format name (json-string)
-             - Possible values: "blkdebug", "bochs", "cloop", "dmg",
-                                "file", "file", "ftp", "ftps", "host_cdrom",
-                                "host_device", "http", "https",
-                                "nbd", "parallels", "qcow", "qcow2", "raw",
-                                "vdi", "vmdk", "vpc", "vvfat"
-         - "backing_file": backing file name (json-string, optional)
-         - "backing_file_depth": number of files in the backing file chain (json-int)
-         - "encrypted": true if encrypted, false otherwise (json-bool)
-         - "bps": limit total bytes per second (json-int)
-         - "bps_rd": limit read bytes per second (json-int)
-         - "bps_wr": limit write bytes per second (json-int)
-         - "iops": limit total I/O operations per second (json-int)
-         - "iops_rd": limit read operations per second (json-int)
-         - "iops_wr": limit write operations per second (json-int)
-         - "bps_max":  total max in bytes (json-int)
-         - "bps_rd_max":  read max in bytes (json-int)
-         - "bps_wr_max":  write max in bytes (json-int)
-         - "iops_max":  total I/O operations max (json-int)
-         - "iops_rd_max":  read I/O operations max (json-int)
-         - "iops_wr_max":  write I/O operations max (json-int)
-         - "iops_size": I/O size when limiting by iops (json-int)
-         - "detect_zeroes": detect and optimize zero writing (json-string)
-             - Possible values: "off", "on", "unmap"
-         - "write_threshold": write offset threshold in bytes, a event will be
-                              emitted if crossed. Zero if disabled (json-int)
-         - "image": the detail of the image, it is a json-object containing
-            the following:
-             - "filename": image file name (json-string)
-             - "format": image format (json-string)
-             - "virtual-size": image capacity in bytes (json-int)
-             - "dirty-flag": true if image is not cleanly closed, not present
-                             means clean (json-bool, optional)
-             - "actual-size": actual size on disk in bytes of the image, not
-                              present when image does not support thin
-                              provision (json-int, optional)
-             - "cluster-size": size of a cluster in bytes, not present if image
-                               format does not support it (json-int, optional)
-             - "encrypted": true if the image is encrypted, not present means
-                            false or the image format does not support
-                            encryption (json-bool, optional)
-             - "backing_file": backing file name, not present means no backing
-                               file is used or the image format does not
-                               support backing file chain
-                               (json-string, optional)
-             - "full-backing-filename": full path of the backing file, not
-                                        present if it equals backing_file or no
-                                        backing file is used
-                                        (json-string, optional)
-             - "backing-filename-format": the format of the backing file, not
-                                          present means unknown or no backing
-                                          file (json-string, optional)
-             - "snapshots": the internal snapshot info, it is an optional list
-                of json-object containing the following:
-                 - "id": unique snapshot id (json-string)
-                 - "name": snapshot name (json-string)
-                 - "vm-state-size": size of the VM state in bytes (json-int)
-                 - "date-sec": UTC date of the snapshot in seconds (json-int)
-                 - "date-nsec": fractional part in nanoseconds to be used with
-                                date-sec (json-int)
-                 - "vm-clock-sec": VM clock relative to boot in seconds
-                                   (json-int)
-                 - "vm-clock-nsec": fractional part in nanoseconds to be used
-                                    with vm-clock-sec (json-int)
-             - "backing-image": the detail of the backing image, it is an
-                                optional json-object only present when a
-                                backing image present for this image
-
-- "io-status": I/O operation status, only present if the device supports it
-               and the VM is configured to stop on errors. It's always reset
-               to "ok" when the "cont" command is issued (json_string, optional)
-             - Possible values: "ok", "failed", "nospace"
-
-Example:
-
--> { "execute": "query-block" }
-<- {
-      "return":[
-         {
-            "io-status": "ok",
-            "device":"ide0-hd0",
-            "locked":false,
-            "removable":false,
-            "inserted":{
-               "ro":false,
-               "drv":"qcow2",
-               "encrypted":false,
-               "file":"disks/test.qcow2",
-               "backing_file_depth":1,
-               "bps":1000000,
-               "bps_rd":0,
-               "bps_wr":0,
-               "iops":1000000,
-               "iops_rd":0,
-               "iops_wr":0,
-               "bps_max": 8000000,
-               "bps_rd_max": 0,
-               "bps_wr_max": 0,
-               "iops_max": 0,
-               "iops_rd_max": 0,
-               "iops_wr_max": 0,
-               "iops_size": 0,
-               "detect_zeroes": "on",
-               "write_threshold": 0,
-               "image":{
-                  "filename":"disks/test.qcow2",
-                  "format":"qcow2",
-                  "virtual-size":2048000,
-                  "backing_file":"base.qcow2",
-                  "full-backing-filename":"disks/base.qcow2",
-                  "backing-filename-format":"qcow2",
-                  "snapshots":[
-                     {
-                        "id": "1",
-                        "name": "snapshot1",
-                        "vm-state-size": 0,
-                        "date-sec": 10000200,
-                        "date-nsec": 12,
-                        "vm-clock-sec": 206,
-                        "vm-clock-nsec": 30
-                     }
-                  ],
-                  "backing-image":{
-                      "filename":"disks/base.qcow2",
-                      "format":"qcow2",
-                      "virtual-size":2048000
-                  }
-               }
-            },
-            "type":"unknown"
-         },
-         {
-            "io-status": "ok",
-            "device":"ide1-cd0",
-            "locked":false,
-            "removable":true,
-            "type":"unknown"
-         },
-         {
-            "device":"floppy0",
-            "locked":false,
-            "removable":true,
-            "type":"unknown"
-         },
-         {
-            "device":"sd0",
-            "locked":false,
-            "removable":true,
-            "type":"unknown"
-         }
-      ]
-   }
-
-query-blockstats
-----------------
-
-Show block device statistics.
-
-Each device statistic information is stored in a json-object and the returned
-value is a json-array of all devices.
-
-Each json-object contain the following:
-
-- "device": device name (json-string)
-- "stats": A json-object with the statistics information, it contains:
-    - "rd_bytes": bytes read (json-int)
-    - "wr_bytes": bytes written (json-int)
-    - "rd_operations": read operations (json-int)
-    - "wr_operations": write operations (json-int)
-    - "flush_operations": cache flush operations (json-int)
-    - "wr_total_time_ns": total time spend on writes in nano-seconds (json-int)
-    - "rd_total_time_ns": total time spend on reads in nano-seconds (json-int)
-    - "flush_total_time_ns": total time spend on cache flushes in nano-seconds (json-int)
-    - "wr_highest_offset": The offset after the greatest byte written to the
-                           BlockDriverState since it has been opened (json-int)
-    - "rd_merged": number of read requests that have been merged into
-                   another request (json-int)
-    - "wr_merged": number of write requests that have been merged into
-                   another request (json-int)
-    - "idle_time_ns": time since the last I/O operation, in
-                      nanoseconds. If the field is absent it means
-                      that there haven't been any operations yet
-                      (json-int, optional)
-    - "failed_rd_operations": number of failed read operations
-                              (json-int)
-    - "failed_wr_operations": number of failed write operations
-                              (json-int)
-    - "failed_flush_operations": number of failed flush operations
-                               (json-int)
-    - "invalid_rd_operations": number of invalid read operations
-                               (json-int)
-    - "invalid_wr_operations": number of invalid write operations
-                               (json-int)
-    - "invalid_flush_operations": number of invalid flush operations
-                                  (json-int)
-    - "account_invalid": whether invalid operations are included in
-                         the last access statistics (json-bool)
-    - "account_failed": whether failed operations are included in the
-                         latency and last access statistics
-                         (json-bool)
-    - "timed_stats": A json-array containing statistics collected in
-                     specific intervals, with the following members:
-        - "interval_length": interval used for calculating the
-                             statistics, in seconds (json-int)
-        - "min_rd_latency_ns": minimum latency of read operations in
-                               the defined interval, in nanoseconds
-                               (json-int)
-        - "min_wr_latency_ns": minimum latency of write operations in
-                               the defined interval, in nanoseconds
-                               (json-int)
-        - "min_flush_latency_ns": minimum latency of flush operations
-                                  in the defined interval, in
-                                  nanoseconds (json-int)
-        - "max_rd_latency_ns": maximum latency of read operations in
-                               the defined interval, in nanoseconds
-                               (json-int)
-        - "max_wr_latency_ns": maximum latency of write operations in
-                               the defined interval, in nanoseconds
-                               (json-int)
-        - "max_flush_latency_ns": maximum latency of flush operations
-                                  in the defined interval, in
-                                  nanoseconds (json-int)
-        - "avg_rd_latency_ns": average latency of read operations in
-                               the defined interval, in nanoseconds
-                               (json-int)
-        - "avg_wr_latency_ns": average latency of write operations in
-                               the defined interval, in nanoseconds
-                               (json-int)
-        - "avg_flush_latency_ns": average latency of flush operations
-                                  in the defined interval, in
-                                  nanoseconds (json-int)
-        - "avg_rd_queue_depth": average number of pending read
-                                operations in the defined interval
-                                (json-number)
-        - "avg_wr_queue_depth": average number of pending write
-                                operations in the defined interval
-                                (json-number).
-- "parent": Contains recursively the statistics of the underlying
-            protocol (e.g. the host file for a qcow2 image). If there is
-            no underlying protocol, this field is omitted
-            (json-object, optional)
-
-Example:
-
--> { "execute": "query-blockstats" }
-<- {
-      "return":[
-         {
-            "device":"ide0-hd0",
-            "parent":{
-               "stats":{
-                  "wr_highest_offset":3686448128,
-                  "wr_bytes":9786368,
-                  "wr_operations":751,
-                  "rd_bytes":122567168,
-                  "rd_operations":36772
-                  "wr_total_times_ns":313253456
-                  "rd_total_times_ns":3465673657
-                  "flush_total_times_ns":49653
-                  "flush_operations":61,
-                  "rd_merged":0,
-                  "wr_merged":0,
-                  "idle_time_ns":2953431879,
-                  "account_invalid":true,
-                  "account_failed":false
-               }
-            },
-            "stats":{
-               "wr_highest_offset":2821110784,
-               "wr_bytes":9786368,
-               "wr_operations":692,
-               "rd_bytes":122739200,
-               "rd_operations":36604
-               "flush_operations":51,
-               "wr_total_times_ns":313253456
-               "rd_total_times_ns":3465673657
-               "flush_total_times_ns":49653,
-               "rd_merged":0,
-               "wr_merged":0,
-               "idle_time_ns":2953431879,
-               "account_invalid":true,
-               "account_failed":false
-            }
-         },
-         {
-            "device":"ide1-cd0",
-            "stats":{
-               "wr_highest_offset":0,
-               "wr_bytes":0,
-               "wr_operations":0,
-               "rd_bytes":0,
-               "rd_operations":0
-               "flush_operations":0,
-               "wr_total_times_ns":0
-               "rd_total_times_ns":0
-               "flush_total_times_ns":0,
-               "rd_merged":0,
-               "wr_merged":0,
-               "account_invalid":false,
-               "account_failed":false
-            }
-         },
-         {
-            "device":"floppy0",
-            "stats":{
-               "wr_highest_offset":0,
-               "wr_bytes":0,
-               "wr_operations":0,
-               "rd_bytes":0,
-               "rd_operations":0
-               "flush_operations":0,
-               "wr_total_times_ns":0
-               "rd_total_times_ns":0
-               "flush_total_times_ns":0,
-               "rd_merged":0,
-               "wr_merged":0,
-               "account_invalid":false,
-               "account_failed":false
-            }
-         },
-         {
-            "device":"sd0",
-            "stats":{
-               "wr_highest_offset":0,
-               "wr_bytes":0,
-               "wr_operations":0,
-               "rd_bytes":0,
-               "rd_operations":0
-               "flush_operations":0,
-               "wr_total_times_ns":0
-               "rd_total_times_ns":0
-               "flush_total_times_ns":0,
-               "rd_merged":0,
-               "wr_merged":0,
-               "account_invalid":false,
-               "account_failed":false
-            }
-         }
-      ]
-   }
-
-query-cpus
-----------
-
-Show CPU information.
-
-Return a json-array. Each CPU is represented by a json-object, which contains:
-
-- "CPU": CPU index (json-int)
-- "current": true if this is the current CPU, false otherwise (json-bool)
-- "halted": true if the cpu is halted, false otherwise (json-bool)
-- "qom_path": path to the CPU object in the QOM tree (json-str)
-- "arch": architecture of the cpu, which determines what additional
-          keys will be present (json-str)
-- Current program counter. The key's name depends on the architecture:
-     "pc": i386/x86_64 (json-int)
-     "nip": PPC (json-int)
-     "pc" and "npc": sparc (json-int)
-     "PC": mips (json-int)
-- "thread_id": ID of the underlying host thread (json-int)
-
-Example:
-
--> { "execute": "query-cpus" }
-<- {
-      "return":[
-         {
-            "CPU":0,
-            "current":true,
-            "halted":false,
-            "qom_path":"/machine/unattached/device[0]",
-            "arch":"x86",
-            "pc":3227107138,
-            "thread_id":3134
-         },
-         {
-            "CPU":1,
-            "current":false,
-            "halted":true,
-            "qom_path":"/machine/unattached/device[2]",
-            "arch":"x86",
-            "pc":7108165,
-            "thread_id":3135
-         }
-      ]
-   }
-
-query-iothreads
----------------
-
-Returns a list of information about each iothread.
-
-Note this list excludes the QEMU main loop thread, which is not declared
-using the -object iothread command-line option.  It is always the main thread
-of the process.
-
-Return a json-array. Each iothread is represented by a json-object, which contains:
-
-- "id": name of iothread (json-str)
-- "thread-id": ID of the underlying host thread (json-int)
-
-Example:
-
--> { "execute": "query-iothreads" }
-<- {
-      "return":[
-         {
-            "id":"iothread0",
-            "thread-id":3134
-         },
-         {
-            "id":"iothread1",
-            "thread-id":3135
-         }
-      ]
-   }
-
-query-pci
----------
-
-PCI buses and devices information.
-
-The returned value is a json-array of all buses. Each bus is represented by
-a json-object, which has a key with a json-array of all PCI devices attached
-to it. Each device is represented by a json-object.
-
-The bus json-object contains the following:
-
-- "bus": bus number (json-int)
-- "devices": a json-array of json-objects, each json-object represents a
-             PCI device
-
-The PCI device json-object contains the following:
-
-- "bus": identical to the parent's bus number (json-int)
-- "slot": slot number (json-int)
-- "function": function number (json-int)
-- "class_info": a json-object containing:
-     - "desc": device class description (json-string, optional)
-     - "class": device class number (json-int)
-- "id": a json-object containing:
-     - "device": device ID (json-int)
-     - "vendor": vendor ID (json-int)
-- "irq": device's IRQ if assigned (json-int, optional)
-- "qdev_id": qdev id string (json-string)
-- "pci_bridge": It's a json-object, only present if this device is a
-                PCI bridge, contains:
-     - "bus": bus number (json-int)
-     - "secondary": secondary bus number (json-int)
-     - "subordinate": subordinate bus number (json-int)
-     - "io_range": I/O memory range information, a json-object with the
-                   following members:
-                 - "base": base address, in bytes (json-int)
-                 - "limit": limit address, in bytes (json-int)
-     - "memory_range": memory range information, a json-object with the
-                       following members:
-                 - "base": base address, in bytes (json-int)
-                 - "limit": limit address, in bytes (json-int)
-     - "prefetchable_range": Prefetchable memory range information, a
-                             json-object with the following members:
-                 - "base": base address, in bytes (json-int)
-                 - "limit": limit address, in bytes (json-int)
-     - "devices": a json-array of PCI devices if there's any attached, each
-                  each element is represented by a json-object, which contains
-                  the same members of the 'PCI device json-object' described
-                  above (optional)
-- "regions": a json-array of json-objects, each json-object represents a
-             memory region of this device
-
-The memory range json-object contains the following:
-
-- "base": base memory address (json-int)
-- "limit": limit value (json-int)
-
-The region json-object can be an I/O region or a memory region, an I/O region
-json-object contains the following:
-
-- "type": "io" (json-string, fixed)
-- "bar": BAR number (json-int)
-- "address": memory address (json-int)
-- "size": memory size (json-int)
-
-A memory region json-object contains the following:
-
-- "type": "memory" (json-string, fixed)
-- "bar": BAR number (json-int)
-- "address": memory address (json-int)
-- "size": memory size (json-int)
-- "mem_type_64": true or false (json-bool)
-- "prefetch": true or false (json-bool)
-
-Example:
-
--> { "execute": "query-pci" }
-<- {
-      "return":[
-         {
-            "bus":0,
-            "devices":[
-               {
-                  "bus":0,
-                  "qdev_id":"",
-                  "slot":0,
-                  "class_info":{
-                     "class":1536,
-                     "desc":"Host bridge"
-                  },
-                  "id":{
-                     "device":32902,
-                     "vendor":4663
-                  },
-                  "function":0,
-                  "regions":[
-
-                  ]
-               },
-               {
-                  "bus":0,
-                  "qdev_id":"",
-                  "slot":1,
-                  "class_info":{
-                     "class":1537,
-                     "desc":"ISA bridge"
-                  },
-                  "id":{
-                     "device":32902,
-                     "vendor":28672
-                  },
-                  "function":0,
-                  "regions":[
-
-                  ]
-               },
-               {
-                  "bus":0,
-                  "qdev_id":"",
-                  "slot":1,
-                  "class_info":{
-                     "class":257,
-                     "desc":"IDE controller"
-                  },
-                  "id":{
-                     "device":32902,
-                     "vendor":28688
-                  },
-                  "function":1,
-                  "regions":[
-                     {
-                        "bar":4,
-                        "size":16,
-                        "address":49152,
-                        "type":"io"
-                     }
-                  ]
-               },
-               {
-                  "bus":0,
-                  "qdev_id":"",
-                  "slot":2,
-                  "class_info":{
-                     "class":768,
-                     "desc":"VGA controller"
-                  },
-                  "id":{
-                     "device":4115,
-                     "vendor":184
-                  },
-                  "function":0,
-                  "regions":[
-                     {
-                        "prefetch":true,
-                        "mem_type_64":false,
-                        "bar":0,
-                        "size":33554432,
-                        "address":4026531840,
-                        "type":"memory"
-                     },
-                     {
-                        "prefetch":false,
-                        "mem_type_64":false,
-                        "bar":1,
-                        "size":4096,
-                        "address":4060086272,
-                        "type":"memory"
-                     },
-                     {
-                        "prefetch":false,
-                        "mem_type_64":false,
-                        "bar":6,
-                        "size":65536,
-                        "address":-1,
-                        "type":"memory"
-                     }
-                  ]
-               },
-               {
-                  "bus":0,
-                  "qdev_id":"",
-                  "irq":11,
-                  "slot":4,
-                  "class_info":{
-                     "class":1280,
-                     "desc":"RAM controller"
-                  },
-                  "id":{
-                     "device":6900,
-                     "vendor":4098
-                  },
-                  "function":0,
-                  "regions":[
-                     {
-                        "bar":0,
-                        "size":32,
-                        "address":49280,
-                        "type":"io"
-                     }
-                  ]
-               }
-            ]
-         }
-      ]
-   }
-
-Note: This example has been shortened as the real response is too long.
-
-query-kvm
----------
-
-Show KVM information.
-
-Return a json-object with the following information:
-
-- "enabled": true if KVM support is enabled, false otherwise (json-bool)
-- "present": true if QEMU has KVM support, false otherwise (json-bool)
-
-Example:
-
--> { "execute": "query-kvm" }
-<- { "return": { "enabled": true, "present": true } }
-
-query-status
-------------
-
-Return a json-object with the following information:
-
-- "running": true if the VM is running, or false if it is paused (json-bool)
-- "singlestep": true if the VM is in single step mode,
-                false otherwise (json-bool)
-- "status": one of the following values (json-string)
-    "debug" - QEMU is running on a debugger
-    "inmigrate" - guest is paused waiting for an incoming migration
-    "internal-error" - An internal error that prevents further guest
-    execution has occurred
-    "io-error" - the last IOP has failed and the device is configured
-    to pause on I/O errors
-    "paused" - guest has been paused via the 'stop' command
-    "postmigrate" - guest is paused following a successful 'migrate'
-    "prelaunch" - QEMU was started with -S and guest has not started
-    "finish-migrate" - guest is paused to finish the migration process
-    "restore-vm" - guest is paused to restore VM state
-    "running" - guest is actively running
-    "save-vm" - guest is paused to save the VM state
-    "shutdown" - guest is shut down (and -no-shutdown is in use)
-    "watchdog" - the watchdog action is configured to pause and
-     has been triggered
-
-Example:
-
--> { "execute": "query-status" }
-<- { "return": { "running": true, "singlestep": false, "status": "running" } }
-
-query-mice
-----------
-
-Show VM mice information.
-
-Each mouse is represented by a json-object, the returned value is a json-array
-of all mice.
-
-The mouse json-object contains the following:
-
-- "name": mouse's name (json-string)
-- "index": mouse's index (json-int)
-- "current": true if this mouse is receiving events, false otherwise (json-bool)
-- "absolute": true if the mouse generates absolute input events (json-bool)
-
-Example:
-
--> { "execute": "query-mice" }
-<- {
-      "return":[
-         {
-            "name":"QEMU Microsoft Mouse",
-            "index":0,
-            "current":false,
-            "absolute":false
-         },
-         {
-            "name":"QEMU PS/2 Mouse",
-            "index":1,
-            "current":true,
-            "absolute":true
-         }
-      ]
-   }
-
-query-vnc
----------
-
-Show VNC server information.
-
-Return a json-object with server information. Connected clients are returned
-as a json-array of json-objects.
-
-The main json-object contains the following:
-
-- "enabled": true or false (json-bool)
-- "host": server's IP address (json-string)
-- "family": address family (json-string)
-         - Possible values: "ipv4", "ipv6", "unix", "unknown"
-- "service": server's port number (json-string)
-- "auth": authentication method (json-string)
-         - Possible values: "invalid", "none", "ra2", "ra2ne", "sasl", "tight",
-                            "tls", "ultra", "unknown", "vencrypt", "vencrypt",
-                            "vencrypt+plain", "vencrypt+tls+none",
-                            "vencrypt+tls+plain", "vencrypt+tls+sasl",
-                            "vencrypt+tls+vnc", "vencrypt+x509+none",
-                            "vencrypt+x509+plain", "vencrypt+x509+sasl",
-                            "vencrypt+x509+vnc", "vnc"
-- "clients": a json-array of all connected clients
-
-Clients are described by a json-object, each one contain the following:
-
-- "host": client's IP address (json-string)
-- "family": address family (json-string)
-         - Possible values: "ipv4", "ipv6", "unix", "unknown"
-- "service": client's port number (json-string)
-- "x509_dname": TLS dname (json-string, optional)
-- "sasl_username": SASL username (json-string, optional)
-
-Example:
-
--> { "execute": "query-vnc" }
-<- {
-      "return":{
-         "enabled":true,
-         "host":"0.0.0.0",
-         "service":"50402",
-         "auth":"vnc",
-         "family":"ipv4",
-         "clients":[
-            {
-               "host":"127.0.0.1",
-               "service":"50401",
-               "family":"ipv4"
-            }
-         ]
-      }
-   }
-
-query-spice
------------
-
-Show SPICE server information.
-
-Return a json-object with server information. Connected clients are returned
-as a json-array of json-objects.
-
-The main json-object contains the following:
-
-- "enabled": true or false (json-bool)
-- "host": server's IP address (json-string)
-- "port": server's port number (json-int, optional)
-- "tls-port": server's port number (json-int, optional)
-- "auth": authentication method (json-string)
-         - Possible values: "none", "spice"
-- "channels": a json-array of all active channels clients
-
-Channels are described by a json-object, each one contain the following:
-
-- "host": client's IP address (json-string)
-- "family": address family (json-string)
-         - Possible values: "ipv4", "ipv6", "unix", "unknown"
-- "port": client's port number (json-string)
-- "connection-id": spice connection id.  All channels with the same id
-                   belong to the same spice session (json-int)
-- "channel-type": channel type.  "1" is the main control channel, filter for
-                  this one if you want track spice sessions only (json-int)
-- "channel-id": channel id.  Usually "0", might be different needed when
-                multiple channels of the same type exist, such as multiple
-                display channels in a multihead setup (json-int)
-- "tls": whether the channel is encrypted (json-bool)
-
-Example:
-
--> { "execute": "query-spice" }
-<- {
-      "return": {
-         "enabled": true,
-         "auth": "spice",
-         "port": 5920,
-         "tls-port": 5921,
-         "host": "0.0.0.0",
-         "channels": [
-            {
-               "port": "54924",
-               "family": "ipv4",
-               "channel-type": 1,
-               "connection-id": 1804289383,
-               "host": "127.0.0.1",
-               "channel-id": 0,
-               "tls": true
-            },
-            {
-               "port": "36710",
-               "family": "ipv4",
-               "channel-type": 4,
-               "connection-id": 1804289383,
-               "host": "127.0.0.1",
-               "channel-id": 0,
-               "tls": false
-            },
-            [ ... more channels follow ... ]
-         ]
-      }
-   }
-
-query-name
-----------
-
-Show VM name.
-
-Return a json-object with the following information:
-
-- "name": VM's name (json-string, optional)
-
-Example:
-
--> { "execute": "query-name" }
-<- { "return": { "name": "qemu-name" } }
-
-query-uuid
-----------
-
-Show VM UUID.
-
-Return a json-object with the following information:
-
-- "UUID": Universally Unique Identifier (json-string)
-
-Example:
-
--> { "execute": "query-uuid" }
-<- { "return": { "UUID": "550e8400-e29b-41d4-a716-446655440000" } }
-
-query-command-line-options
---------------------------
-
-Show command line option schema.
-
-Return a json-array of command line option schema for all options (or for
-the given option), returning an error if the given option doesn't exist.
-
-Each array entry contains the following:
-
-- "option": option name (json-string)
-- "parameters": a json-array describes all parameters of the option:
-    - "name": parameter name (json-string)
-    - "type": parameter type (one of 'string', 'boolean', 'number',
-              or 'size')
-    - "help": human readable description of the parameter
-              (json-string, optional)
-    - "default": default value string for the parameter
-                 (json-string, optional)
-
-Example:
-
--> { "execute": "query-command-line-options", "arguments": { "option": "option-rom" } }
-<- { "return": [
-        {
-            "parameters": [
-                {
-                    "name": "romfile",
-                    "type": "string"
-                },
-                {
-                    "name": "bootindex",
-                    "type": "number"
-                }
-            ],
-            "option": "option-rom"
-        }
-     ]
-   }
-
-query-migrate
--------------
-
-Migration status.
-
-Return a json-object. If migration is active there will be another json-object
-with RAM migration status and if block migration is active another one with
-block migration status.
-
-The main json-object contains the following:
-
-- "status": migration status (json-string)
-     - Possible values: "setup", "active", "completed", "failed", "cancelled"
-- "total-time": total amount of ms since migration started.  If
-                migration has ended, it returns the total migration
-                time (json-int)
-- "setup-time" amount of setup time in milliseconds _before_ the
-               iterations begin but _after_ the QMP command is issued.
-               This is designed to provide an accounting of any activities
-               (such as RDMA pinning) which may be expensive, but do not
-               actually occur during the iterative migration rounds
-               themselves. (json-int)
-- "downtime": only present when migration has finished correctly
-              total amount in ms for downtime that happened (json-int)
-- "expected-downtime": only present while migration is active
-                total amount in ms for downtime that was calculated on
-                the last bitmap round (json-int)
-- "ram": only present if "status" is "active", it is a json-object with the
-  following RAM information:
-         - "transferred": amount transferred in bytes (json-int)
-         - "remaining": amount remaining to transfer in bytes (json-int)
-         - "total": total amount of memory in bytes (json-int)
-         - "duplicate": number of pages filled entirely with the same
-            byte (json-int)
-            These are sent over the wire much more efficiently.
-         - "skipped": number of skipped zero pages (json-int)
-         - "normal" : number of whole pages transferred.  I.e. they
-            were not sent as duplicate or xbzrle pages (json-int)
-         - "normal-bytes" : number of bytes transferred in whole
-            pages. This is just normal pages times size of one page,
-            but this way upper levels don't need to care about page
-            size (json-int)
-         - "dirty-sync-count": times that dirty ram was synchronized (json-int)
-- "disk": only present if "status" is "active" and it is a block migration,
-  it is a json-object with the following disk information:
-         - "transferred": amount transferred in bytes (json-int)
-         - "remaining": amount remaining to transfer in bytes json-int)
-         - "total": total disk size in bytes (json-int)
-- "xbzrle-cache": only present if XBZRLE is active.
-  It is a json-object with the following XBZRLE information:
-         - "cache-size": XBZRLE cache size in bytes
-         - "bytes": number of bytes transferred for XBZRLE compressed pages
-         - "pages": number of XBZRLE compressed pages
-         - "cache-miss": number of XBRZRLE page cache misses
-         - "cache-miss-rate": rate of XBRZRLE page cache misses
-         - "overflow": number of times XBZRLE overflows.  This means
-           that the XBZRLE encoding was bigger than just sent the
-           whole page, and then we sent the whole page instead (as as
-           normal page).
-
-Examples:
-
-1. Before the first migration
-
--> { "execute": "query-migrate" }
-<- { "return": {} }
-
-2. Migration is done and has succeeded
-
--> { "execute": "query-migrate" }
-<- { "return": {
-        "status": "completed",
-        "ram":{
-          "transferred":123,
-          "remaining":123,
-          "total":246,
-          "total-time":12345,
-          "setup-time":12345,
-          "downtime":12345,
-          "duplicate":123,
-          "normal":123,
-          "normal-bytes":123456,
-          "dirty-sync-count":15
-        }
-     }
-   }
-
-3. Migration is done and has failed
-
--> { "execute": "query-migrate" }
-<- { "return": { "status": "failed" } }
-
-4. Migration is being performed and is not a block migration:
-
--> { "execute": "query-migrate" }
-<- {
-      "return":{
-         "status":"active",
-         "ram":{
-            "transferred":123,
-            "remaining":123,
-            "total":246,
-            "total-time":12345,
-            "setup-time":12345,
-            "expected-downtime":12345,
-            "duplicate":123,
-            "normal":123,
-            "normal-bytes":123456,
-            "dirty-sync-count":15
-         }
-      }
-   }
-
-5. Migration is being performed and is a block migration:
-
--> { "execute": "query-migrate" }
-<- {
-      "return":{
-         "status":"active",
-         "ram":{
-            "total":1057024,
-            "remaining":1053304,
-            "transferred":3720,
-            "total-time":12345,
-            "setup-time":12345,
-            "expected-downtime":12345,
-            "duplicate":123,
-            "normal":123,
-            "normal-bytes":123456,
-            "dirty-sync-count":15
-         },
-         "disk":{
-            "total":20971520,
-            "remaining":20880384,
-            "transferred":91136
-         }
-      }
-   }
-
-6. Migration is being performed and XBZRLE is active:
-
--> { "execute": "query-migrate" }
-<- {
-      "return":{
-         "status":"active",
-         "capabilities" : [ { "capability": "xbzrle", "state" : true } ],
-         "ram":{
-            "total":1057024,
-            "remaining":1053304,
-            "transferred":3720,
-            "total-time":12345,
-            "setup-time":12345,
-            "expected-downtime":12345,
-            "duplicate":10,
-            "normal":3333,
-            "normal-bytes":3412992,
-            "dirty-sync-count":15
-         },
-         "xbzrle-cache":{
-            "cache-size":67108864,
-            "bytes":20971520,
-            "pages":2444343,
-            "cache-miss":2244,
-            "cache-miss-rate":0.123,
-            "overflow":34434
-         }
-      }
-   }
-
-migrate-set-capabilities
-------------------------
-
-Enable/Disable migration capabilities
-
-- "xbzrle": XBZRLE support
-- "rdma-pin-all": pin all pages when using RDMA during migration
-- "auto-converge": throttle down guest to help convergence of migration
-- "zero-blocks": compress zero blocks during block migration
-- "compress": use multiple compression threads to accelerate live migration
-- "events": generate events for each migration state change
-- "postcopy-ram": postcopy mode for live migration
-- "x-colo": COarse-Grain LOck Stepping (COLO) for Non-stop Service
-
-Arguments:
-
-Example:
-
--> { "execute": "migrate-set-capabilities" , "arguments":
-     { "capabilities": [ { "capability": "xbzrle", "state": true } ] } }
-
-query-migrate-capabilities
---------------------------
-
-Query current migration capabilities
-
-- "capabilities": migration capabilities state
-         - "xbzrle" : XBZRLE state (json-bool)
-         - "rdma-pin-all" : RDMA Pin Page state (json-bool)
-         - "auto-converge" : Auto Converge state (json-bool)
-         - "zero-blocks" : Zero Blocks state (json-bool)
-         - "compress": Multiple compression threads state (json-bool)
-         - "events": Migration state change event state (json-bool)
-         - "postcopy-ram": postcopy ram state (json-bool)
-         - "x-colo": COarse-Grain LOck Stepping for Non-stop Service (json-bool)
-
-Arguments:
-
-Example:
-
--> { "execute": "query-migrate-capabilities" }
-<- {"return": [
-     {"state": false, "capability": "xbzrle"},
-     {"state": false, "capability": "rdma-pin-all"},
-     {"state": false, "capability": "auto-converge"},
-     {"state": false, "capability": "zero-blocks"},
-     {"state": false, "capability": "compress"},
-     {"state": true, "capability": "events"},
-     {"state": false, "capability": "postcopy-ram"},
-     {"state": false, "capability": "x-colo"}
-   ]}
-
-migrate-set-parameters
-----------------------
-
-Set migration parameters
-
-- "compress-level": set compression level during migration (json-int)
-- "compress-threads": set compression thread count for migration (json-int)
-- "decompress-threads": set decompression thread count for migration (json-int)
-- "cpu-throttle-initial": set initial percentage of time guest cpus are
-                          throttled for auto-converge (json-int)
-- "cpu-throttle-increment": set throttle increasing percentage for
-                            auto-converge (json-int)
-- "max-bandwidth": set maximum speed for migrations (in bytes/sec) (json-int)
-- "downtime-limit": set maximum tolerated downtime (in milliseconds) for
-                    migrations (json-int)
-- "x-checkpoint-delay": set the delay time for periodic checkpoint (json-int)
-
-Arguments:
-
-Example:
-
--> { "execute": "migrate-set-parameters" , "arguments":
-      { "compress-level": 1 } }
-
-query-migrate-parameters
-------------------------
-
-Query current migration parameters
-
-- "parameters": migration parameters value
-         - "compress-level" : compression level value (json-int)
-         - "compress-threads" : compression thread count value (json-int)
-         - "decompress-threads" : decompression thread count value (json-int)
-         - "cpu-throttle-initial" : initial percentage of time guest cpus are
-                                    throttled (json-int)
-         - "cpu-throttle-increment" : throttle increasing percentage for
-                                      auto-converge (json-int)
-         - "max-bandwidth" : maximium migration speed in bytes per second
-                             (json-int)
-         - "downtime-limit" : maximum tolerated downtime of migration in
-                              milliseconds (json-int)
-Arguments:
-
-Example:
-
--> { "execute": "query-migrate-parameters" }
-<- {
-      "return": {
-         "decompress-threads": 2,
-         "cpu-throttle-increment": 10,
-         "compress-threads": 8,
-         "compress-level": 1,
-         "cpu-throttle-initial": 20,
-         "max-bandwidth": 33554432,
-         "downtime-limit": 300
-      }
-   }
-
-query-balloon
--------------
-
-Show balloon information.
-
-Make an asynchronous request for balloon info. When the request completes a
-json-object will be returned containing the following data:
-
-- "actual": current balloon value in bytes (json-int)
-
-Example:
-
--> { "execute": "query-balloon" }
-<- {
-      "return":{
-         "actual":1073741824,
-      }
-   }
-
-query-tpm
----------
-
-Return information about the TPM device.
-
-Arguments: None
-
-Example:
-
--> { "execute": "query-tpm" }
-<- { "return":
-     [
-       { "model": "tpm-tis",
-         "options":
-           { "type": "passthrough",
-             "data":
-               { "cancel-path": "/sys/class/misc/tpm0/device/cancel",
-                 "path": "/dev/tpm0"
-               }
-           },
-         "id": "tpm0"
-       }
-     ]
-   }
-
-query-tpm-models
-----------------
-
-Return a list of supported TPM models.
-
-Arguments: None
-
-Example:
-
--> { "execute": "query-tpm-models" }
-<- { "return": [ "tpm-tis" ] }
-
-query-tpm-types
----------------
-
-Return a list of supported TPM types.
-
-Arguments: None
-
-Example:
-
--> { "execute": "query-tpm-types" }
-<- { "return": [ "passthrough" ] }
-
-chardev-add
-----------------
-
-Add a chardev.
-
-Arguments:
-
-- "id": the chardev's ID, must be unique (json-string)
-- "backend": chardev backend type + parameters
-
-Examples:
-
--> { "execute" : "chardev-add",
-     "arguments" : { "id" : "foo",
-                     "backend" : { "type" : "null", "data" : {} } } }
-<- { "return": {} }
-
--> { "execute" : "chardev-add",
-     "arguments" : { "id" : "bar",
-                     "backend" : { "type" : "file",
-                                   "data" : { "out" : "/tmp/bar.log" } } } }
-<- { "return": {} }
-
--> { "execute" : "chardev-add",
-     "arguments" : { "id" : "baz",
-                     "backend" : { "type" : "pty", "data" : {} } } }
-<- { "return": { "pty" : "/dev/pty/42" } }
-
-chardev-remove
---------------
-
-Remove a chardev.
-
-Arguments:
-
-- "id": the chardev's ID, must exist and not be in use (json-string)
-
-Example:
-
--> { "execute": "chardev-remove", "arguments": { "id" : "foo" } }
-<- { "return": {} }
-
-query-rx-filter
----------------
-
-Show rx-filter information.
-
-Returns a json-array of rx-filter information for all NICs (or for the
-given NIC), returning an error if the given NIC doesn't exist, or
-given NIC doesn't support rx-filter querying, or given net client
-isn't a NIC.
-
-The query will clear the event notification flag of each NIC, then qemu
-will start to emit event to QMP monitor.
-
-Each array entry contains the following:
-
-- "name": net client name (json-string)
-- "promiscuous": promiscuous mode is enabled (json-bool)
-- "multicast": multicast receive state (one of 'normal', 'none', 'all')
-- "unicast": unicast receive state  (one of 'normal', 'none', 'all')
-- "vlan": vlan receive state (one of 'normal', 'none', 'all') (Since 2.0)
-- "broadcast-allowed": allow to receive broadcast (json-bool)
-- "multicast-overflow": multicast table is overflowed (json-bool)
-- "unicast-overflow": unicast table is overflowed (json-bool)
-- "main-mac": main macaddr string (json-string)
-- "vlan-table": a json-array of active vlan id
-- "unicast-table": a json-array of unicast macaddr string
-- "multicast-table": a json-array of multicast macaddr string
-
-Example:
-
--> { "execute": "query-rx-filter", "arguments": { "name": "vnet0" } }
-<- { "return": [
-        {
-            "promiscuous": true,
-            "name": "vnet0",
-            "main-mac": "52:54:00:12:34:56",
-            "unicast": "normal",
-            "vlan": "normal",
-            "vlan-table": [
-                4,
-                0
-            ],
-            "unicast-table": [
-            ],
-            "multicast": "normal",
-            "multicast-overflow": false,
-            "unicast-overflow": false,
-            "multicast-table": [
-                "01:00:5e:00:00:01",
-                "33:33:00:00:00:01",
-                "33:33:ff:12:34:56"
-            ],
-            "broadcast-allowed": false
-        }
-      ]
-   }
-
-blockdev-add
-------------
-
-Add a block device.
-
-This command is still a work in progress.  It doesn't support all
-block drivers among other things.  Stay away from it unless you want
-to help with its development.
-
-For the arguments, see the QAPI schema documentation of BlockdevOptions.
-
-Example (1):
-
--> { "execute": "blockdev-add",
-    "arguments": { "driver": "qcow2",
-                   "file": { "driver": "file",
-                             "filename": "test.qcow2" } } }
-<- { "return": {} }
-
-Example (2):
-
--> { "execute": "blockdev-add",
-     "arguments": {
-         "driver": "qcow2",
-         "node-name": "my_disk",
-         "discard": "unmap",
-         "cache": {
-             "direct": true,
-             "writeback": true
-         },
-         "file": {
-             "driver": "file",
-             "filename": "/tmp/test.qcow2"
-         },
-         "backing": {
-             "driver": "raw",
-             "file": {
-                 "driver": "file",
-                 "filename": "/dev/fdset/4"
-             }
-         }
-       }
-     }
-
-<- { "return": {} }
-
-x-blockdev-del
-------------
-Since 2.5
-
-Deletes a block device that has been added using blockdev-add.
-The command will fail if the node is attached to a device or is
-otherwise being used.
-
-This command is still a work in progress and is considered
-experimental. Stay away from it unless you want to help with its
-development.
-
-Arguments:
-
-- "node-name": Name of the graph node to delete (json-string)
-
-Example:
-
--> { "execute": "blockdev-add",
-     "arguments": {
-         "driver": "qcow2",
-         "node-name": "node0",
-         "file": {
-             "driver": "file",
-             "filename": "test.qcow2"
-         }
-     }
-   }
-
-<- { "return": {} }
-
--> { "execute": "x-blockdev-del",
-     "arguments": { "node-name": "node0" }
-   }
-<- { "return": {} }
-
-blockdev-open-tray
-------------------
-
-Opens a block device's tray. If there is a block driver state tree inserted as a
-medium, it will become inaccessible to the guest (but it will remain associated
-to the block device, so closing the tray will make it accessible again).
-
-If the tray was already open before, this will be a no-op.
-
-Once the tray opens, a DEVICE_TRAY_MOVED event is emitted. There are cases in
-which no such event will be generated, these include:
-- if the guest has locked the tray, @force is false and the guest does not
-  respond to the eject request
-- if the BlockBackend denoted by @device does not have a guest device attached
-  to it
-- if the guest device does not have an actual tray and is empty, for instance
-  for floppy disk drives
-
-Arguments:
-
-- "device": block device name (deprecated, use @id instead)
-            (json-string, optional)
-- "id": the name or QOM path of the guest device (json-string, optional)
-- "force": if false (the default), an eject request will be sent to the guest if
-           it has locked the tray (and the tray will not be opened immediately);
-           if true, the tray will be opened regardless of whether it is locked
-           (json-bool, optional)
-
-Example:
-
--> { "execute": "blockdev-open-tray",
-     "arguments": { "id": "ide0-1-0" } }
-
-<- { "timestamp": { "seconds": 1418751016,
-                    "microseconds": 716996 },
-     "event": "DEVICE_TRAY_MOVED",
-     "data": { "device": "ide1-cd0",
-               "id": "ide0-1-0",
-               "tray-open": true } }
-
-<- { "return": {} }
-
-blockdev-close-tray
--------------------
-
-Closes a block device's tray. If there is a block driver state tree associated
-with the block device (which is currently ejected), that tree will be loaded as
-the medium.
-
-If the tray was already closed before, this will be a no-op.
-
-Arguments:
-
-- "device": block device name (deprecated, use @id instead)
-            (json-string, optional)
-- "id": the name or QOM path of the guest device (json-string, optional)
-
-Example:
-
--> { "execute": "blockdev-close-tray",
-     "arguments": { "id": "ide0-1-0" } }
-
-<- { "timestamp": { "seconds": 1418751345,
-                    "microseconds": 272147 },
-     "event": "DEVICE_TRAY_MOVED",
-     "data": { "device": "ide1-cd0",
-               "id": "ide0-1-0",
-               "tray-open": false } }
-
-<- { "return": {} }
-
-x-blockdev-remove-medium
-------------------------
-
-Removes a medium (a block driver state tree) from a block device. That block
-device's tray must currently be open (unless there is no attached guest device).
-
-If the tray is open and there is no medium inserted, this will be a no-op.
-
-This command is still a work in progress and is considered experimental.
-Stay away from it unless you want to help with its development.
-
-Arguments:
-
-- "device": block device name (deprecated, use @id instead)
-            (json-string, optional)
-- "id": the name or QOM path of the guest device (json-string, optional)
-
-Example:
-
--> { "execute": "x-blockdev-remove-medium",
-     "arguments": { "id": "ide0-1-0" } }
-
-<- { "error": { "class": "GenericError",
-                "desc": "Tray of device 'ide0-1-0' is not open" } }
-
--> { "execute": "blockdev-open-tray",
-     "arguments": { "id": "ide0-1-0" } }
-
-<- { "timestamp": { "seconds": 1418751627,
-                    "microseconds": 549958 },
-     "event": "DEVICE_TRAY_MOVED",
-     "data": { "device": "ide1-cd0",
-               "id": "ide0-1-0",
-               "tray-open": true } }
-
-<- { "return": {} }
-
--> { "execute": "x-blockdev-remove-medium",
-     "arguments": { "device": "ide0-1-0" } }
-
-<- { "return": {} }
-
-x-blockdev-insert-medium
-------------------------
-
-Inserts a medium (a block driver state tree) into a block device. That block
-device's tray must currently be open (unless there is no attached guest device)
-and there must be no medium inserted already.
-
-This command is still a work in progress and is considered experimental.
-Stay away from it unless you want to help with its development.
-
-Arguments:
-
-- "device": block device name (deprecated, use @id instead)
-            (json-string, optional)
-- "id": the name or QOM path of the guest device (json-string, optional)
-- "node-name": root node of the BDS tree to insert into the block device
-
-Example:
-
--> { "execute": "blockdev-add",
-     "arguments": { { "node-name": "node0",
-                      "driver": "raw",
-                      "file": { "driver": "file",
-                                "filename": "fedora.iso" } } }
-
-<- { "return": {} }
-
--> { "execute": "x-blockdev-insert-medium",
-     "arguments": { "id": "ide0-1-0",
-                    "node-name": "node0" } }
-
-<- { "return": {} }
-
-x-blockdev-change
------------------
-
-Dynamically reconfigure the block driver state graph. It can be used
-to add, remove, insert or replace a graph node. Currently only the
-Quorum driver implements this feature to add or remove its child. This
-is useful to fix a broken quorum child.
-
-If @node is specified, it will be inserted under @parent. @child
-may not be specified in this case. If both @parent and @child are
-specified but @node is not, @child will be detached from @parent.
-
-Arguments:
-- "parent": the id or name of the parent node (json-string)
-- "child": the name of a child under the given parent node (json-string, optional)
-- "node": the name of the node that will be added (json-string, optional)
-
-Note: this command is experimental, and not a stable API. It doesn't
-support all kinds of operations, all kinds of children, nor all block
-drivers.
-
-Warning: The data in a new quorum child MUST be consistent with that of
-the rest of the array.
-
-Example:
-
-Add a new node to a quorum
--> { "execute": "blockdev-add",
-     "arguments": { "driver": "raw",
-                    "node-name": "new_node",
-                    "file": { "driver": "file",
-                              "filename": "test.raw" } } }
-<- { "return": {} }
--> { "execute": "x-blockdev-change",
-     "arguments": { "parent": "disk1",
-                    "node": "new_node" } }
-<- { "return": {} }
-
-Delete a quorum's node
--> { "execute": "x-blockdev-change",
-     "arguments": { "parent": "disk1",
-                    "child": "children.1" } }
-<- { "return": {} }
-
-query-named-block-nodes
------------------------
-
-Return a list of BlockDeviceInfo for all the named block driver nodes
-
-Example:
-
--> { "execute": "query-named-block-nodes" }
-<- { "return": [ { "ro":false,
-                   "drv":"qcow2",
-                   "encrypted":false,
-                   "file":"disks/test.qcow2",
-                   "node-name": "my-node",
-                   "backing_file_depth":1,
-                   "bps":1000000,
-                   "bps_rd":0,
-                   "bps_wr":0,
-                   "iops":1000000,
-                   "iops_rd":0,
-                   "iops_wr":0,
-                   "bps_max": 8000000,
-                   "bps_rd_max": 0,
-                   "bps_wr_max": 0,
-                   "iops_max": 0,
-                   "iops_rd_max": 0,
-                   "iops_wr_max": 0,
-                   "iops_size": 0,
-                   "write_threshold": 0,
-                   "image":{
-                      "filename":"disks/test.qcow2",
-                      "format":"qcow2",
-                      "virtual-size":2048000,
-                      "backing_file":"base.qcow2",
-                      "full-backing-filename":"disks/base.qcow2",
-                      "backing-filename-format":"qcow2",
-                      "snapshots":[
-                         {
-                            "id": "1",
-                            "name": "snapshot1",
-                            "vm-state-size": 0,
-                            "date-sec": 10000200,
-                            "date-nsec": 12,
-                            "vm-clock-sec": 206,
-                            "vm-clock-nsec": 30
-                         }
-                      ],
-                      "backing-image":{
-                          "filename":"disks/base.qcow2",
-                          "format":"qcow2",
-                          "virtual-size":2048000
-                      }
-                   } } ] }
-
-blockdev-change-medium
-----------------------
-
-Changes the medium inserted into a block device by ejecting the current medium
-and loading a new image file which is inserted as the new medium.
-
-Arguments:
-
-- "device": block device name (deprecated, use @id instead)
-            (json-string, optional)
-- "id": the name or QOM path of the guest device (json-string, optional)
-- "filename": filename of the new image (json-string)
-- "format": format of the new image (json-string, optional)
-- "read-only-mode": new read-only mode (json-string, optional)
-          - Possible values: "retain" (default), "read-only", "read-write"
-
-Examples:
-
-1. Change a removable medium
-
--> { "execute": "blockdev-change-medium",
-             "arguments": { "id": "ide0-1-0",
-                            "filename": "/srv/images/Fedora-12-x86_64-DVD.iso",
-                            "format": "raw" } }
-<- { "return": {} }
-
-2. Load a read-only medium into a writable drive
-
--> { "execute": "blockdev-change-medium",
-             "arguments": { "id": "floppyA",
-                            "filename": "/srv/images/ro.img",
-                            "format": "raw",
-                            "read-only-mode": "retain" } }
-
-<- { "error":
-     { "class": "GenericError",
-       "desc": "Could not open '/srv/images/ro.img': Permission denied" } }
-
--> { "execute": "blockdev-change-medium",
-             "arguments": { "id": "floppyA",
-                            "filename": "/srv/images/ro.img",
-                            "format": "raw",
-                            "read-only-mode": "read-only" } }
-
-<- { "return": {} }
-
-query-memdev
-------------
-
-Show memory devices information.
-
-
-Example (1):
-
--> { "execute": "query-memdev" }
-<- { "return": [
-       {
-         "size": 536870912,
-         "merge": false,
-         "dump": true,
-         "prealloc": false,
-         "host-nodes": [0, 1],
-         "policy": "bind"
-       },
-       {
-         "size": 536870912,
-         "merge": false,
-         "dump": true,
-         "prealloc": true,
-         "host-nodes": [2, 3],
-         "policy": "preferred"
-       }
-     ]
-   }
-
-query-memory-devices
---------------------
-
-Return a list of memory devices.
-
-Example:
--> { "execute": "query-memory-devices" }
-<- { "return": [ { "data":
-                      { "addr": 5368709120,
-                        "hotpluggable": true,
-                        "hotplugged": true,
-                        "id": "d1",
-                        "memdev": "/objects/memX",
-                        "node": 0,
-                        "size": 1073741824,
-                        "slot": 0},
-                   "type": "dimm"
-                 } ] }
-
-query-acpi-ospm-status
-----------------------
-
-Return list of ACPIOSTInfo for devices that support status reporting
-via ACPI _OST method.
-
-Example:
--> { "execute": "query-acpi-ospm-status" }
-<- { "return": [ { "device": "d1", "slot": "0", "slot-type": "DIMM", "source": 1, "status": 0},
-                 { "slot": "1", "slot-type": "DIMM", "source": 0, "status": 0},
-                 { "slot": "2", "slot-type": "DIMM", "source": 0, "status": 0},
-                 { "slot": "3", "slot-type": "DIMM", "source": 0, "status": 0}
-   ]}
-
-rtc-reset-reinjection
----------------------
-
-Reset the RTC interrupt reinjection backlog.
-
-Arguments: None.
-
-Example:
-
--> { "execute": "rtc-reset-reinjection" }
-<- { "return": {} }
-
-trace-event-get-state
----------------------
-
-Query the state of events.
-
-Arguments:
-
-- "name": Event name pattern (json-string).
-- "vcpu": The vCPU to query, any vCPU by default (json-int, optional).
-
-An event is returned if:
-- its name matches the "name" pattern, and
-- if "vcpu" is given, the event has the "vcpu" property.
-
-Therefore, if "vcpu" is given, the operation will only match per-vCPU events,
-returning their state on the specified vCPU. Special case: if "name" is an exact
-match, "vcpu" is given and the event does not have the "vcpu" property, an error
-is returned.
-
-Example:
-
--> { "execute": "trace-event-get-state", "arguments": { "name": "qemu_memalign" } }
-<- { "return": [ { "name": "qemu_memalign", "state": "disabled" } ] }
-
-trace-event-set-state
----------------------
-
-Set the state of events.
-
-Arguments:
-
-- "name": Event name pattern (json-string).
-- "enable": Whether to enable or disable the event (json-bool).
-- "ignore-unavailable": Whether to ignore errors for events that cannot be
-  changed (json-bool, optional).
-- "vcpu": The vCPU to act upon, all vCPUs by default (json-int, optional).
-
-An event's state is modified if:
-- its name matches the "name" pattern, and
-- if "vcpu" is given, the event has the "vcpu" property.
-
-Therefore, if "vcpu" is given, the operation will only match per-vCPU events,
-setting their state on the specified vCPU. Special case: if "name" is an exact
-match, "vcpu" is given and the event does not have the "vcpu" property, an error
-is returned.
-
-Example:
-
--> { "execute": "trace-event-set-state", "arguments": { "name": "qemu_memalign", "enable": "true" } }
-<- { "return": {} }
-
-input-send-event
-----------------
-
-Send input event to guest.
-
-Arguments:
-
-- "device": display device (json-string, optional)
-- "head": display head (json-int, optional)
-- "events": list of input events
-
-The consoles are visible in the qom tree, under
-/backend/console[$index]. They have a device link and head property, so
-it is possible to map which console belongs to which device and display.
-
-Example (1):
-
-Press left mouse button.
-
--> { "execute": "input-send-event",
-    "arguments": { "device": "video0",
-                   "events": [ { "type": "btn",
-                   "data" : { "down": true, "button": "left" } } ] } }
-<- { "return": {} }
-
--> { "execute": "input-send-event",
-    "arguments": { "device": "video0",
-                   "events": [ { "type": "btn",
-                   "data" : { "down": false, "button": "left" } } ] } }
-<- { "return": {} }
-
-Example (2):
-
-Press ctrl-alt-del.
-
--> { "execute": "input-send-event",
-     "arguments": { "events": [
-        { "type": "key", "data" : { "down": true,
-          "key": {"type": "qcode", "data": "ctrl" } } },
-        { "type": "key", "data" : { "down": true,
-          "key": {"type": "qcode", "data": "alt" } } },
-        { "type": "key", "data" : { "down": true,
-          "key": {"type": "qcode", "data": "delete" } } } ] } }
-<- { "return": {} }
-
-Example (3):
-
-Move mouse pointer to absolute coordinates (20000, 400).
-
--> { "execute": "input-send-event" ,
-  "arguments": { "events": [
-               { "type": "abs", "data" : { "axis": "x", "value" : 20000 } },
-               { "type": "abs", "data" : { "axis": "y", "value" : 400 } } ] } }
-<- { "return": {} }
-
-block-set-write-threshold
-------------
-
-Change the write threshold for a block drive. The threshold is an offset,
-thus must be non-negative. Default is no write threshold.
-Setting the threshold to zero disables it.
-
-Arguments:
-
-- "node-name": the node name in the block driver state graph (json-string)
-- "write-threshold": the write threshold in bytes (json-int)
-
-Example:
-
--> { "execute": "block-set-write-threshold",
-  "arguments": { "node-name": "mydev",
-                 "write-threshold": 17179869184 } }
-<- { "return": {} }
-
-Show rocker switch
-------------------
-
-Arguments:
-
-- "name": switch name
-
-Example:
-
--> { "execute": "query-rocker", "arguments": { "name": "sw1" } }
-<- { "return": {"name": "sw1", "ports": 2, "id": 1327446905938}}
-
-Show rocker switch ports
-------------------------
-
-Arguments:
-
-- "name": switch name
-
-Example:
-
--> { "execute": "query-rocker-ports", "arguments": { "name": "sw1" } }
-<- { "return": [ {"duplex": "full", "enabled": true, "name": "sw1.1",
-                  "autoneg": "off", "link-up": true, "speed": 10000},
-                 {"duplex": "full", "enabled": true, "name": "sw1.2",
-                  "autoneg": "off", "link-up": true, "speed": 10000}
-   ]}
-
-Show rocker switch OF-DPA flow tables
--------------------------------------
-
-Arguments:
-
-- "name": switch name
-- "tbl-id": (optional) flow table ID
-
-Example:
-
--> { "execute": "query-rocker-of-dpa-flows", "arguments": { "name": "sw1" } }
-<- { "return": [ {"key": {"in-pport": 0, "priority": 1, "tbl-id": 0},
-                  "hits": 138,
-                  "cookie": 0,
-                  "action": {"goto-tbl": 10},
-                  "mask": {"in-pport": 4294901760}
-                 },
-                 {...more...},
-   ]}
-
-Show rocker OF-DPA group tables
--------------------------------
-
-Arguments:
-
-- "name": switch name
-- "type": (optional) group type
-
-Example:
-
--> { "execute": "query-rocker-of-dpa-groups", "arguments": { "name": "sw1" } }
-<- { "return": [ {"type": 0, "out-pport": 2, "pport": 2, "vlan-id": 3841,
-                  "pop-vlan": 1, "id": 251723778},
-                 {"type": 0, "out-pport": 0, "pport": 0, "vlan-id": 3841,
-                  "pop-vlan": 1, "id": 251723776},
-                 {"type": 0, "out-pport": 1, "pport": 1, "vlan-id": 3840,
-                  "pop-vlan": 1, "id": 251658241},
-                 {"type": 0, "out-pport": 0, "pport": 0, "vlan-id": 3840,
-                  "pop-vlan": 1, "id": 251658240}
-   ]}
-
-query-gic-capabilities
----------------
-
-Return a list of GICCapability objects, describing supported GIC
-(Generic Interrupt Controller) versions.
-
-Arguments: None
-
-Example:
-
--> { "execute": "query-gic-capabilities" }
-<- { "return": [{ "version": 2, "emulated": true, "kernel": false },
-                { "version": 3, "emulated": false, "kernel": true } ] }
-
-Show existing/possible CPUs
----------------------------
-
-Arguments: None.
-
-Example for pseries machine type started with
--smp 2,cores=2,maxcpus=4 -cpu POWER8:
-
--> { "execute": "query-hotpluggable-cpus" }
-<- {"return": [
-     { "props": { "core-id": 8 }, "type": "POWER8-spapr-cpu-core",
-       "vcpus-count": 1 },
-     { "props": { "core-id": 0 }, "type": "POWER8-spapr-cpu-core",
-       "vcpus-count": 1, "qom-path": "/machine/unattached/device[0]"}
-   ]}'
-
-Example for pc machine type started with
--smp 1,maxcpus=2:
-    -> { "execute": "query-hotpluggable-cpus" }
-    <- {"return": [
-         {
-            "type": "qemu64-x86_64-cpu", "vcpus-count": 1,
-            "props": {"core-id": 0, "socket-id": 1, "thread-id": 0}
-         },
-         {
-            "qom-path": "/machine/unattached/device[0]",
-            "type": "qemu64-x86_64-cpu", "vcpus-count": 1,
-            "props": {"core-id": 0, "socket-id": 0, "thread-id": 0}
-         }
-       ]}
diff --git a/docs/qmp-events.txt b/docs/qmp-events.txt
deleted file mode 100644
index e0a2365..0000000
--- a/docs/qmp-events.txt
+++ /dev/null
@@ -1,731 +0,0 @@
-                   QEMU Machine Protocol Events
-                   ============================
-
-ACPI_DEVICE_OST
----------------
-
-Emitted when guest executes ACPI _OST method.
-
- - data: ACPIOSTInfo type as described in qapi-schema.json
-
-{ "event": "ACPI_DEVICE_OST",
-     "data": { "device": "d1", "slot": "0", "slot-type": "DIMM", "source": 1, "status": 0 } }
-
-BALLOON_CHANGE
---------------
-
-Emitted when the guest changes the actual BALLOON level. This
-value is equivalent to the 'actual' field return by the
-'query-balloon' command
-
-Data:
-
-- "actual": actual level of the guest memory balloon in bytes (json-number)
-
-Example:
-
-{ "event": "BALLOON_CHANGE",
-    "data": { "actual": 944766976 },
-    "timestamp": { "seconds": 1267020223, "microseconds": 435656 } }
-
-Note: this event is rate-limited.
-
-BLOCK_IMAGE_CORRUPTED
----------------------
-
-Emitted when a disk image is being marked corrupt. The image can be
-identified by its device or node name. The 'device' field is always
-present for compatibility reasons, but it can be empty ("") if the
-image does not have a device name associated.
-
-Data:
-
-- "device":    Device name (json-string)
-- "node-name": Node name (json-string, optional)
-- "msg":       Informative message (e.g., reason for the corruption)
-               (json-string)
-- "offset":    If the corruption resulted from an image access, this
-               is the host's access offset into the image
-               (json-int, optional)
-- "size":      If the corruption resulted from an image access, this
-               is the access size (json-int, optional)
-
-Example:
-
-{ "event": "BLOCK_IMAGE_CORRUPTED",
-    "data": { "device": "ide0-hd0", "node-name": "node0",
-        "msg": "Prevented active L1 table overwrite", "offset": 196608,
-        "size": 65536 },
-    "timestamp": { "seconds": 1378126126, "microseconds": 966463 } }
-
-BLOCK_IO_ERROR
---------------
-
-Emitted when a disk I/O error occurs.
-
-Data:
-
-- "device": device name. This is always present for compatibility
-            reasons, but it can be empty ("") if the image does not
-            have a device name associated. (json-string)
-- "node-name": node name. Note that errors may be reported for the root node
-               that is directly attached to a guest device rather than for the
-               node where the error occurred. (json-string)
-- "operation": I/O operation (json-string, "read" or "write")
-- "action": action that has been taken, it's one of the following (json-string):
-    "ignore": error has been ignored
-    "report": error has been reported to the device
-    "stop": the VM is going to stop because of the error
-
-Example:
-
-{ "event": "BLOCK_IO_ERROR",
-    "data": { "device": "ide0-hd1",
-              "node-name": "#block212",
-              "operation": "write",
-              "action": "stop" },
-    "timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
-
-Note: If action is "stop", a STOP event will eventually follow the
-BLOCK_IO_ERROR event.
-
-BLOCK_JOB_CANCELLED
--------------------
-
-Emitted when a block job has been cancelled.
-
-Data:
-
-- "type":     Job type (json-string; "stream" for image streaming
-                                     "commit" for block commit)
-- "device":   Job identifier. Originally the device name but other
-              values are allowed since QEMU 2.7 (json-string)
-- "len":      Maximum progress value (json-int)
-- "offset":   Current progress value (json-int)
-              On success this is equal to len.
-              On failure this is less than len.
-- "speed":    Rate limit, bytes per second (json-int)
-
-Example:
-
-{ "event": "BLOCK_JOB_CANCELLED",
-     "data": { "type": "stream", "device": "virtio-disk0",
-               "len": 10737418240, "offset": 134217728,
-               "speed": 0 },
-     "timestamp": { "seconds": 1267061043, "microseconds": 959568 } }
-
-BLOCK_JOB_COMPLETED
--------------------
-
-Emitted when a block job has completed.
-
-Data:
-
-- "type":     Job type (json-string; "stream" for image streaming
-                                     "commit" for block commit)
-- "device":   Job identifier. Originally the device name but other
-              values are allowed since QEMU 2.7 (json-string)
-- "len":      Maximum progress value (json-int)
-- "offset":   Current progress value (json-int)
-              On success this is equal to len.
-              On failure this is less than len.
-- "speed":    Rate limit, bytes per second (json-int)
-- "error":    Error message (json-string, optional)
-              Only present on failure.  This field contains a human-readable
-              error message.  There are no semantics other than that streaming
-              has failed and clients should not try to interpret the error
-              string.
-
-Example:
-
-{ "event": "BLOCK_JOB_COMPLETED",
-     "data": { "type": "stream", "device": "virtio-disk0",
-               "len": 10737418240, "offset": 10737418240,
-               "speed": 0 },
-     "timestamp": { "seconds": 1267061043, "microseconds": 959568 } }
-
-BLOCK_JOB_ERROR
----------------
-
-Emitted when a block job encounters an error.
-
-Data:
-
-- "device": Job identifier. Originally the device name but other
-            values are allowed since QEMU 2.7 (json-string)
-- "operation": I/O operation (json-string, "read" or "write")
-- "action": action that has been taken, it's one of the following (json-string):
-    "ignore": error has been ignored, the job may fail later
-    "report": error will be reported and the job canceled
-    "stop": error caused job to be paused
-
-Example:
-
-{ "event": "BLOCK_JOB_ERROR",
-    "data": { "device": "ide0-hd1",
-              "operation": "write",
-              "action": "stop" },
-    "timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
-
-BLOCK_JOB_READY
----------------
-
-Emitted when a block job is ready to complete.
-
-Data:
-
-- "type":     Job type (json-string; "stream" for image streaming
-                                     "commit" for block commit)
-- "device":   Job identifier. Originally the device name but other
-              values are allowed since QEMU 2.7 (json-string)
-- "len":      Maximum progress value (json-int)
-- "offset":   Current progress value (json-int)
-              On success this is equal to len.
-              On failure this is less than len.
-- "speed":    Rate limit, bytes per second (json-int)
-
-Example:
-
-{ "event": "BLOCK_JOB_READY",
-    "data": { "device": "drive0", "type": "mirror", "speed": 0,
-              "len": 2097152, "offset": 2097152 }
-    "timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
-
-Note: The "ready to complete" status is always reset by a BLOCK_JOB_ERROR
-event.
-
-DEVICE_DELETED
---------------
-
-Emitted whenever the device removal completion is acknowledged
-by the guest.
-At this point, it's safe to reuse the specified device ID.
-Device removal can be initiated by the guest or by HMP/QMP commands.
-
-Data:
-
-- "device": device name (json-string, optional)
-- "path": device path (json-string)
-
-{ "event": "DEVICE_DELETED",
-  "data": { "device": "virtio-net-pci-0",
-            "path": "/machine/peripheral/virtio-net-pci-0" },
-  "timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
-
-DEVICE_TRAY_MOVED
------------------
-
-It's emitted whenever the tray of a removable device is moved by the guest
-or by HMP/QMP commands.
-
-Data:
-
-- "device": Block device name. This is always present for compatibility
-            reasons, but it can be empty ("") if the image does not have a
-            device name associated. (json-string)
-- "id": The name or QOM path of the guest device (json-string)
-- "tray-open": true if the tray has been opened or false if it has been closed
-               (json-bool)
-
-{ "event": "DEVICE_TRAY_MOVED",
-  "data": { "device": "ide1-cd0",
-            "id": "/machine/unattached/device[22]",
-            "tray-open": true
-  },
-  "timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
-
-DUMP_COMPLETED
---------------
-
-Emitted when the guest has finished one memory dump.
-
-Data:
-
-- "result": DumpQueryResult type described in qapi-schema.json
-- "error": Error message when dump failed. This is only a
-  human-readable string provided when dump failed. It should not be
-  parsed in any way (json-string, optional)
-
-Example:
-
-{ "event": "DUMP_COMPLETED",
-  "data": {"result": {"total": 1090650112, "status": "completed",
-                      "completed": 1090650112} } }
-
-GUEST_PANICKED
---------------
-
-Emitted when guest OS panic is detected.
-
-Data:
-
-- "action": Action that has been taken (json-string, currently always "pause").
-
-Example:
-
-{ "event": "GUEST_PANICKED",
-     "data": { "action": "pause" } }
-
-MEM_UNPLUG_ERROR
---------------------
-Emitted when memory hot unplug error occurs.
-
-Data:
-
-- "device": device name (json-string)
-- "msg": Informative message (e.g., reason for the error) (json-string)
-
-Example:
-
-{ "event": "MEM_UNPLUG_ERROR"
-  "data": { "device": "dimm1",
-            "msg": "acpi: device unplug for unsupported device"
-  },
-  "timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
-
-NIC_RX_FILTER_CHANGED
----------------------
-
-The event is emitted once until the query command is executed,
-the first event will always be emitted.
-
-Data:
-
-- "name": net client name (json-string)
-- "path": device path (json-string)
-
-{ "event": "NIC_RX_FILTER_CHANGED",
-  "data": { "name": "vnet0",
-            "path": "/machine/peripheral/vnet0/virtio-backend" },
-  "timestamp": { "seconds": 1368697518, "microseconds": 326866 } }
-}
-
-POWERDOWN
----------
-
-Emitted when the Virtual Machine is powered down through the power
-control system, such as via ACPI.
-
-Data: None.
-
-Example:
-
-{ "event": "POWERDOWN",
-    "timestamp": { "seconds": 1267040730, "microseconds": 682951 } }
-
-QUORUM_FAILURE
---------------
-
-Emitted by the Quorum block driver if it fails to establish a quorum.
-
-Data:
-
-- "reference":     device name if defined else node name.
-- "sector-num":    Number of the first sector of the failed read operation.
-- "sectors-count": Failed read operation sector count.
-
-Example:
-
-{ "event": "QUORUM_FAILURE",
-     "data": { "reference": "usr1", "sector-num": 345435, "sectors-count": 5 },
-     "timestamp": { "seconds": 1344522075, "microseconds": 745528 } }
-
-Note: this event is rate-limited.
-
-QUORUM_REPORT_BAD
------------------
-
-Emitted to report a corruption of a Quorum file.
-
-Data:
-
-- "type":          Quorum operation type
-- "error":         Error message (json-string, optional)
-                   Only present on failure.  This field contains a human-readable
-                   error message.  There are no semantics other than that the
-                   block layer reported an error and clients should not try to
-                   interpret the error string.
-- "node-name":     The graph node name of the block driver state.
-- "sector-num":    Number of the first sector of the failed read operation.
-- "sectors-count": Failed read operation sector count.
-
-Example:
-
-Read operation:
-{ "event": "QUORUM_REPORT_BAD",
-     "data": { "node-name": "node0", "sector-num": 345435, "sectors-count": 5,
-               "type": "read" },
-     "timestamp": { "seconds": 1344522075, "microseconds": 745528 } }
-
-Flush operation:
-{ "event": "QUORUM_REPORT_BAD",
-     "data": { "node-name": "node0", "sector-num": 0, "sectors-count": 2097120,
-               "type": "flush", "error": "Broken pipe" },
-     "timestamp": { "seconds": 1456406829, "microseconds": 291763 } }
-
-Note: this event is rate-limited.
-
-RESET
------
-
-Emitted when the Virtual Machine is reset.
-
-Data: None.
-
-Example:
-
-{ "event": "RESET",
-    "timestamp": { "seconds": 1267041653, "microseconds": 9518 } }
-
-RESUME
-------
-
-Emitted when the Virtual Machine resumes execution.
-
-Data: None.
-
-Example:
-
-{ "event": "RESUME",
-    "timestamp": { "seconds": 1271770767, "microseconds": 582542 } }
-
-RTC_CHANGE
-----------
-
-Emitted when the guest changes the RTC time.
-
-Data:
-
-- "offset": Offset between base RTC clock (as specified by -rtc base), and
-new RTC clock value (json-number)
-
-Example:
-
-{ "event": "RTC_CHANGE",
-    "data": { "offset": 78 },
-    "timestamp": { "seconds": 1267020223, "microseconds": 435656 } }
-
-Note: this event is rate-limited.
-
-SHUTDOWN
---------
-
-Emitted when the Virtual Machine has shut down, indicating that qemu
-is about to exit.
-
-Data: None.
-
-Example:
-
-{ "event": "SHUTDOWN",
-    "timestamp": { "seconds": 1267040730, "microseconds": 682951 } }
-
-Note: If the command-line option "-no-shutdown" has been specified, a STOP
-event will eventually follow the SHUTDOWN event.
-
-SPICE_CONNECTED
----------------
-
-Emitted when a SPICE client connects.
-
-Data:
-
-- "server": Server information (json-object)
-  - "host": IP address (json-string)
-  - "port": port number (json-string)
-  - "family": address family (json-string, "ipv4" or "ipv6")
-- "client": Client information (json-object)
-  - "host": IP address (json-string)
-  - "port": port number (json-string)
-  - "family": address family (json-string, "ipv4" or "ipv6")
-
-Example:
-
-{ "timestamp": {"seconds": 1290688046, "microseconds": 388707},
-  "event": "SPICE_CONNECTED",
-  "data": {
-    "server": { "port": "5920", "family": "ipv4", "host": "127.0.0.1"},
-    "client": {"port": "52873", "family": "ipv4", "host": "127.0.0.1"}
-}}
-
-SPICE_DISCONNECTED
-------------------
-
-Emitted when a SPICE client disconnects.
-
-Data:
-
-- "server": Server information (json-object)
-  - "host": IP address (json-string)
-  - "port": port number (json-string)
-  - "family": address family (json-string, "ipv4" or "ipv6")
-- "client": Client information (json-object)
-  - "host": IP address (json-string)
-  - "port": port number (json-string)
-  - "family": address family (json-string, "ipv4" or "ipv6")
-
-Example:
-
-{ "timestamp": {"seconds": 1290688046, "microseconds": 388707},
-  "event": "SPICE_DISCONNECTED",
-  "data": {
-    "server": { "port": "5920", "family": "ipv4", "host": "127.0.0.1"},
-    "client": {"port": "52873", "family": "ipv4", "host": "127.0.0.1"}
-}}
-
-SPICE_INITIALIZED
------------------
-
-Emitted after initial handshake and authentication takes place (if any)
-and the SPICE channel is up and running
-
-Data:
-
-- "server": Server information (json-object)
-  - "host": IP address (json-string)
-  - "port": port number (json-string)
-  - "family": address family (json-string, "ipv4" or "ipv6")
-  - "auth": authentication method (json-string, optional)
-- "client": Client information (json-object)
-  - "host": IP address (json-string)
-  - "port": port number (json-string)
-  - "family": address family (json-string, "ipv4" or "ipv6")
-  - "connection-id": spice connection id.  All channels with the same id
-                     belong to the same spice session (json-int)
-  - "channel-type": channel type.  "1" is the main control channel, filter for
-                    this one if you want track spice sessions only (json-int)
-  - "channel-id": channel id.  Usually "0", might be different needed when
-                  multiple channels of the same type exist, such as multiple
-                  display channels in a multihead setup (json-int)
-  - "tls": whevener the channel is encrypted (json-bool)
-
-Example:
-
-{ "timestamp": {"seconds": 1290688046, "microseconds": 417172},
-  "event": "SPICE_INITIALIZED",
-  "data": {"server": {"auth": "spice", "port": "5921",
-                      "family": "ipv4", "host": "127.0.0.1"},
-           "client": {"port": "49004", "family": "ipv4", "channel-type": 3,
-                      "connection-id": 1804289383, "host": "127.0.0.1",
-                      "channel-id": 0, "tls": true}
-}}
-
-SPICE_MIGRATE_COMPLETED
------------------------
-
-Emitted when SPICE migration has completed
-
-Data: None.
-
-Example:
-
-{ "timestamp": {"seconds": 1290688046, "microseconds": 417172},
-  "event": "SPICE_MIGRATE_COMPLETED" }
-
-MIGRATION
----------
-
-Emitted when a migration event happens
-
-Data: None.
-
- - "status": migration status
-     See MigrationStatus in ~/qapi-schema.json for possible values
-
-Example:
-
-{"timestamp": {"seconds": 1432121972, "microseconds": 744001},
- "event": "MIGRATION", "data": {"status": "completed"}}
-
-MIGRATION_PASS
---------------
-
-Emitted from the source side of a migration at the start of each pass
-(when it syncs the dirty bitmap)
-
-Data: None.
-
-  - "pass": An incrementing count (starting at 1 on the first pass)
-
-Example:
-{"timestamp": {"seconds": 1449669631, "microseconds": 239225},
- "event": "MIGRATION_PASS", "data": {"pass": 2}}
-
-STOP
-----
-
-Emitted when the Virtual Machine is stopped.
-
-Data: None.
-
-Example:
-
-{ "event": "STOP",
-    "timestamp": { "seconds": 1267041730, "microseconds": 281295 } }
-
-SUSPEND
--------
-
-Emitted when guest enters S3 state.
-
-Data: None.
-
-Example:
-
-{ "event": "SUSPEND",
-     "timestamp": { "seconds": 1344456160, "microseconds": 309119 } }
-
-SUSPEND_DISK
-------------
-
-Emitted when the guest makes a request to enter S4 state.
-
-Data: None.
-
-Example:
-
-{ "event": "SUSPEND_DISK",
-     "timestamp": { "seconds": 1344456160, "microseconds": 309119 } }
-
-Note: QEMU shuts down when entering S4 state.
-
-VNC_CONNECTED
--------------
-
-Emitted when a VNC client establishes a connection.
-
-Data:
-
-- "server": Server information (json-object)
-  - "host": IP address (json-string)
-  - "service": port number (json-string)
-  - "family": address family (json-string, "ipv4" or "ipv6")
-  - "auth": authentication method (json-string, optional)
-- "client": Client information (json-object)
-  - "host": IP address (json-string)
-  - "service": port number (json-string)
-  - "family": address family (json-string, "ipv4" or "ipv6")
-
-Example:
-
-{ "event": "VNC_CONNECTED",
-    "data": {
-        "server": { "auth": "sasl", "family": "ipv4",
-                    "service": "5901", "host": "0.0.0.0" },
-        "client": { "family": "ipv4", "service": "58425",
-                    "host": "127.0.0.1" } },
-    "timestamp": { "seconds": 1262976601, "microseconds": 975795 } }
-
-
-Note: This event is emitted before any authentication takes place, thus
-the authentication ID is not provided.
-
-VNC_DISCONNECTED
-----------------
-
-Emitted when the connection is closed.
-
-Data:
-
-- "server": Server information (json-object)
-  - "host": IP address (json-string)
-  - "service": port number (json-string)
-  - "family": address family (json-string, "ipv4" or "ipv6")
-  - "auth": authentication method (json-string, optional)
-- "client": Client information (json-object)
-  - "host": IP address (json-string)
-  - "service": port number (json-string)
-  - "family": address family (json-string, "ipv4" or "ipv6")
-  - "x509_dname": TLS dname (json-string, optional)
-  - "sasl_username": SASL username (json-string, optional)
-
-Example:
-
-{ "event": "VNC_DISCONNECTED",
-    "data": {
-        "server": { "auth": "sasl", "family": "ipv4",
-                    "service": "5901", "host": "0.0.0.0" },
-        "client": { "family": "ipv4", "service": "58425",
-                    "host": "127.0.0.1", "sasl_username": "luiz" } },
-    "timestamp": { "seconds": 1262976601, "microseconds": 975795 } }
-
-VNC_INITIALIZED
----------------
-
-Emitted after authentication takes place (if any) and the VNC session is
-made active.
-
-Data:
-
-- "server": Server information (json-object)
-  - "host": IP address (json-string)
-  - "service": port number (json-string)
-  - "family": address family (json-string, "ipv4" or "ipv6")
-  - "auth": authentication method (json-string, optional)
-- "client": Client information (json-object)
-  - "host": IP address (json-string)
-  - "service": port number (json-string)
-  - "family": address family (json-string, "ipv4" or "ipv6")
-  - "x509_dname": TLS dname (json-string, optional)
-  - "sasl_username": SASL username (json-string, optional)
-
-Example:
-
-{ "event": "VNC_INITIALIZED",
-    "data": {
-        "server": { "auth": "sasl", "family": "ipv4",
-                    "service": "5901", "host": "0.0.0.0"},
-        "client": { "family": "ipv4", "service": "46089",
-                    "host": "127.0.0.1", "sasl_username": "luiz" } },
-        "timestamp": { "seconds": 1263475302, "microseconds": 150772 } }
-
-VSERPORT_CHANGE
----------------
-
-Emitted when the guest opens or closes a virtio-serial port.
-
-Data:
-
-- "id": device identifier of the virtio-serial port (json-string)
-- "open": true if the guest has opened the virtio-serial port (json-bool)
-
-Example:
-
-{ "event": "VSERPORT_CHANGE",
-    "data": { "id": "channel0", "open": true },
-    "timestamp": { "seconds": 1401385907, "microseconds": 422329 } }
-
-Note: this event is rate-limited separately for each "id".
-
-WAKEUP
-------
-
-Emitted when the guest has woken up from S3 and is running.
-
-Data: None.
-
-Example:
-
-{ "event": "WAKEUP",
-     "timestamp": { "seconds": 1344522075, "microseconds": 745528 } }
-
-WATCHDOG
---------
-
-Emitted when the watchdog device's timer is expired.
-
-Data:
-
-- "action": Action that has been taken, it's one of the following (json-string):
-            "reset", "shutdown", "poweroff", "pause", "debug", or "none"
-
-Example:
-
-{ "event": "WATCHDOG",
-     "data": { "action": "reset" },
-     "timestamp": { "seconds": 1267061043, "microseconds": 959568 } }
-
-Note: If action is "reset", "shutdown", or "pause" the WATCHDOG event is
-followed respectively by the RESET, SHUTDOWN, or STOP events.
-
-Note: this event is rate-limited.
diff --git a/qapi-schema.json b/qapi-schema.json
index 94f1a2e..441112b 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -1,6 +1,53 @@
 # -*- Mode: Python -*-
+##
+# = Introduction
+#
+# This document describes all commands currently supported by QMP.
+#
+# Most of the time their usage is exactly the same as in the user Monitor, this
+# means that any other document which also describe commands (the manpage,
+# QEMU's manual, etc) can and should be consulted.
+#
+# QMP has two types of commands: regular and query commands. Regular commands
+# usually change the Virtual Machine's state someway, while query commands just
+# return information. The sections below are divided accordingly.
+#
+# It's important to observe that all communication examples are formatted in
+# a reader-friendly way, so that they're easier to understand. However, in real
+# protocol usage, they're emitted as a single line.
+#
+# Also, the following notation is used to denote data flow:
+#
+# Example:
+#
+# | -> data issued by the Client
+# | <- Server data response
+#
+# Please, refer to the QMP specification (docs/qmp-spec.txt) for
+# detailed information on the Server command and response formats.
+#
+# = Stability Considerations
+#
+# The current QMP command set (described in this file) may be useful for a
+# number of use cases, however it's limited and several commands have bad
+# defined semantics, specially with regard to command completion.
+#
+# These problems are going to be solved incrementally in the next QEMU releases
+# and we're going to establish a deprecation policy for badly defined commands.
+#
+# If you're planning to adopt QMP, please observe the following:
 #
-# QAPI Schema
+#     1. The deprecation policy will take effect and be documented soon, please
+#        check the documentation of each used command as soon as a new release of
+#        QEMU is available
+#
+#     2. DO NOT rely on anything which is not explicit documented
+#
+#     3. Errors, in special, are not documented. Applications should NOT check
+#        for specific errors classes or data (it's strongly recommended to only
+#        check for the "error" key)
+#
+##
 
 # QAPI common definitions
 { 'include': 'qapi/common.json' }
@@ -89,6 +136,13 @@
 # Returns: nothing on success.
 #
 # Since: 0.14.0
+#
+# Example:
+#
+# -> { "execute": "add_client", "arguments": { "protocol": "vnc",
+#                                              "fdname": "myclient" } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'add_client',
   'data': { 'protocol': 'str', 'fdname': 'str', '*skipauth': 'bool',
@@ -113,6 +167,12 @@
 # Returns: @NameInfo of the guest
 #
 # Since: 0.14.0
+#
+# Example:
+#
+# -> { "execute": "query-name" }
+# <- { "return": { "name": "qemu-name" } }
+#
 ##
 { 'command': 'query-name', 'returns': 'NameInfo' }
 
@@ -137,6 +197,12 @@
 # Returns: @KvmInfo
 #
 # Since: 0.14.0
+#
+# Example:
+#
+# -> { "execute": "query-kvm" }
+# <- { "return": { "enabled": true, "present": true } }
+#
 ##
 { 'command': 'query-kvm', 'returns': 'KvmInfo' }
 
@@ -216,13 +282,21 @@
 # Returns: @StatusInfo reflecting all VCPUs
 #
 # Since:  0.14.0
+#
+# Example:
+#
+# -> { "execute": "query-status" }
+# <- { "return": { "running": true,
+#                  "singlestep": false,
+#                  "status": "running" } }
+#
 ##
 { 'command': 'query-status', 'returns': 'StatusInfo' }
 
 ##
 # @UuidInfo:
 #
-# Guest UUID information.
+# Guest UUID information (Universally Unique Identifier).
 #
 # @UUID: the UUID of the guest
 #
@@ -240,6 +314,12 @@
 # Returns: The @UuidInfo for the guest
 #
 # Since: 0.14.0
+#
+# Example:
+#
+# -> { "execute": "query-uuid" }
+# <- { "return": { "UUID": "550e8400-e29b-41d4-a716-446655440000" } }
+#
 ##
 { 'command': 'query-uuid', 'returns': 'UuidInfo' }
 
@@ -273,6 +353,30 @@
 # Returns: a list of @ChardevInfo
 #
 # Since: 0.14.0
+#
+# Example:
+#
+# -> { "execute": "query-chardev" }
+# <- {
+#       "return": [
+#          {
+#             "label": "charchannel0",
+#             "filename": "unix:/var/lib/libvirt/qemu/seabios.rhel6.agent,server",
+#             "frontend-open": false
+#          },
+#          {
+#             "label": "charmonitor",
+#             "filename": "unix:/var/lib/libvirt/qemu/seabios.rhel6.monitor,server",
+#             "frontend-open": true
+#          },
+#          {
+#             "label": "charserial0",
+#             "filename": "pty:/dev/pts/2",
+#             "frontend-open": true
+#          }
+#       ]
+#    }
+#
 ##
 { 'command': 'query-chardev', 'returns': ['ChardevInfo'] }
 
@@ -295,6 +399,27 @@
 # Returns: a list of @ChardevBackendInfo
 #
 # Since: 2.0
+#
+# Example:
+#
+# -> { "execute": "query-chardev-backends" }
+# <- {
+#       "return":[
+#          {
+#             "name":"udp"
+#          },
+#          {
+#             "name":"tcp"
+#          },
+#          {
+#             "name":"unix"
+#          },
+#          {
+#             "name":"spiceport"
+#          }
+#       ]
+#    }
+#
 ##
 { 'command': 'query-chardev-backends', 'returns': ['ChardevBackendInfo'] }
 
@@ -331,6 +456,15 @@
 # Returns: Nothing on success
 #
 # Since: 1.4
+#
+# Example:
+#
+# -> { "execute": "ringbuf-write",
+#      "arguments": { "device": "foo",
+#                     "data": "abcdefgh",
+#                     "format": "utf8" } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'ringbuf-write',
   'data': {'device': 'str', 'data': 'str',
@@ -358,6 +492,15 @@
 # Returns: data read from the device
 #
 # Since: 1.4
+#
+# Example:
+#
+# -> { "execute": "ringbuf-read",
+#      "arguments": { "device": "foo",
+#                     "size": 1000,
+#                     "format": "utf8" } }
+# <- { "return": "abcdefgh" }
+#
 ##
 { 'command': 'ringbuf-read',
   'data': {'device': 'str', 'size': 'int', '*format': 'DataFormat'},
@@ -382,6 +525,23 @@
 # Returns: A list of @EventInfo for all supported events
 #
 # Since: 1.2.0
+#
+# Example:
+#
+# -> { "execute": "query-events" }
+# <- {
+#      "return": [
+#          {
+#             "name":"SHUTDOWN"
+#          },
+#          {
+#             "name":"RESET"
+#          }
+#       ]
+#    }
+#
+# Note: This example has been shortened as the real response is too long.
+#
 ##
 { 'command': 'query-events', 'returns': ['EventInfo'] }
 
@@ -540,11 +700,124 @@
 ##
 # @query-migrate:
 #
-# Returns information about current migration process.
+# Returns information about current migration process. If migration
+# is active there will be another json-object with RAM migration
+# status and if block migration is active another one with block
+# migration status.
 #
 # Returns: @MigrationInfo
 #
 # Since: 0.14.0
+#
+# Example:
+#
+# 1. Before the first migration
+#
+# -> { "execute": "query-migrate" }
+# <- { "return": {} }
+#
+# 2. Migration is done and has succeeded
+#
+# -> { "execute": "query-migrate" }
+# <- { "return": {
+#         "status": "completed",
+#         "ram":{
+#           "transferred":123,
+#           "remaining":123,
+#           "total":246,
+#           "total-time":12345,
+#           "setup-time":12345,
+#           "downtime":12345,
+#           "duplicate":123,
+#           "normal":123,
+#           "normal-bytes":123456,
+#           "dirty-sync-count":15
+#         }
+#      }
+#    }
+#
+# 3. Migration is done and has failed
+#
+# -> { "execute": "query-migrate" }
+# <- { "return": { "status": "failed" } }
+#
+# 4. Migration is being performed and is not a block migration:
+#
+# -> { "execute": "query-migrate" }
+# <- {
+#       "return":{
+#          "status":"active",
+#          "ram":{
+#             "transferred":123,
+#             "remaining":123,
+#             "total":246,
+#             "total-time":12345,
+#             "setup-time":12345,
+#             "expected-downtime":12345,
+#             "duplicate":123,
+#             "normal":123,
+#             "normal-bytes":123456,
+#             "dirty-sync-count":15
+#          }
+#       }
+#    }
+#
+# 5. Migration is being performed and is a block migration:
+#
+# -> { "execute": "query-migrate" }
+# <- {
+#       "return":{
+#          "status":"active",
+#          "ram":{
+#             "total":1057024,
+#             "remaining":1053304,
+#             "transferred":3720,
+#             "total-time":12345,
+#             "setup-time":12345,
+#             "expected-downtime":12345,
+#             "duplicate":123,
+#             "normal":123,
+#             "normal-bytes":123456,
+#             "dirty-sync-count":15
+#          },
+#          "disk":{
+#             "total":20971520,
+#             "remaining":20880384,
+#             "transferred":91136
+#          }
+#       }
+#    }
+#
+# 6. Migration is being performed and XBZRLE is active:
+#
+# -> { "execute": "query-migrate" }
+# <- {
+#       "return":{
+#          "status":"active",
+#          "capabilities" : [ { "capability": "xbzrle", "state" : true } ],
+#          "ram":{
+#             "total":1057024,
+#             "remaining":1053304,
+#             "transferred":3720,
+#             "total-time":12345,
+#             "setup-time":12345,
+#             "expected-downtime":12345,
+#             "duplicate":10,
+#             "normal":3333,
+#             "normal-bytes":3412992,
+#             "dirty-sync-count":15
+#          },
+#          "xbzrle-cache":{
+#             "cache-size":67108864,
+#             "bytes":20971520,
+#             "pages":2444343,
+#             "cache-miss":2244,
+#             "cache-miss-rate":0.123,
+#             "overflow":34434
+#          }
+#       }
+#    }
+#
 ##
 { 'command': 'query-migrate', 'returns': 'MigrationInfo' }
 
@@ -618,6 +891,12 @@
 # @capabilities: json array of capability modifications to make
 #
 # Since: 1.2
+#
+# Example:
+#
+# -> { "execute": "migrate-set-capabilities" , "arguments":
+#      { "capabilities": [ { "capability": "xbzrle", "state": true } ] } }
+#
 ##
 { 'command': 'migrate-set-capabilities',
   'data': { 'capabilities': ['MigrationCapabilityStatus'] } }
@@ -630,6 +909,21 @@
 # Returns: @MigrationCapabilitiesStatus
 #
 # Since: 1.2
+#
+# Example:
+#
+# -> { "execute": "query-migrate-capabilities" }
+# <- { "return": [
+#       {"state": false, "capability": "xbzrle"},
+#       {"state": false, "capability": "rdma-pin-all"},
+#       {"state": false, "capability": "auto-converge"},
+#       {"state": false, "capability": "zero-blocks"},
+#       {"state": false, "capability": "compress"},
+#       {"state": true, "capability": "events"},
+#       {"state": false, "capability": "postcopy-ram"},
+#       {"state": false, "capability": "x-colo"}
+#    ]}
+#
 ##
 { 'command': 'query-migrate-capabilities', 'returns':   ['MigrationCapabilityStatus']}
 
@@ -698,6 +992,12 @@
 # Set various migration parameters.  See MigrationParameters for details.
 #
 # Since: 2.4
+#
+# Example:
+#
+# -> { "execute": "migrate-set-parameters" ,
+#      "arguments": { "compress-level": 1 } }
+#
 ##
 { 'command': 'migrate-set-parameters', 'boxed': true,
   'data': 'MigrationParameters' }
@@ -769,6 +1069,21 @@
 # Returns: @MigrationParameters
 #
 # Since: 2.4
+#
+# Example:
+#
+# -> { "execute": "query-migrate-parameters" }
+# <- { "return": {
+#          "decompress-threads": 2,
+#          "cpu-throttle-increment": 10,
+#          "compress-threads": 8,
+#          "compress-level": 1,
+#          "cpu-throttle-initial": 20,
+#          "max-bandwidth": 33554432,
+#          "downtime-limit": 300
+#       }
+#    }
+#
 ##
 { 'command': 'query-migrate-parameters',
   'returns': 'MigrationParameters' }
@@ -787,6 +1102,15 @@
 # @cert-subject: #optional server certificate subject
 #
 # Since: 0.14.0
+#
+# Example:
+#
+# -> { "execute": "client_migrate_info",
+#      "arguments": { "protocol": "spice",
+#                     "hostname": "virt42.lab.kraxel.org",
+#                     "port": 1234 } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'client_migrate_info',
   'data': { 'protocol': 'str', 'hostname': 'str', '*port': 'int',
@@ -800,6 +1124,12 @@
 # command.
 #
 # Since: 2.5
+#
+# Example:
+#
+# -> { "execute": "migrate-start-postcopy" }
+# <- { "return": {} }
+#
 ##
 { 'command': 'migrate-start-postcopy' }
 
@@ -872,6 +1202,12 @@
 # then takes over server operation to become the service VM.
 #
 # Since: 2.8
+#
+# Example:
+#
+# -> { "execute": "x-colo-lost-heartbeat" }
+# <- { "return": {} }
+#
 ##
 { 'command': 'x-colo-lost-heartbeat' }
 
@@ -902,6 +1238,26 @@
 # Returns: a list of @MouseInfo for each device
 #
 # Since: 0.14.0
+#
+# Example:
+#
+# -> { "execute": "query-mice" }
+# <- { "return": [
+#          {
+#             "name":"QEMU Microsoft Mouse",
+#             "index":0,
+#             "current":false,
+#             "absolute":false
+#          },
+#          {
+#             "name":"QEMU PS/2 Mouse",
+#             "index":1,
+#             "current":true,
+#             "absolute":true
+#          }
+#       ]
+#    }
+#
 ##
 { 'command': 'query-mice', 'returns': ['MouseInfo'] }
 
@@ -1026,6 +1382,32 @@
 # Returns: a list of @CpuInfo for each virtual CPU
 #
 # Since: 0.14.0
+#
+# Example:
+#
+# -> { "execute": "query-cpus" }
+# <- { "return": [
+#          {
+#             "CPU":0,
+#             "current":true,
+#             "halted":false,
+#             "qom_path":"/machine/unattached/device[0]",
+#             "arch":"x86",
+#             "pc":3227107138,
+#             "thread_id":3134
+#          },
+#          {
+#             "CPU":1,
+#             "current":false,
+#             "halted":true,
+#             "qom_path":"/machine/unattached/device[2]",
+#             "arch":"x86",
+#             "pc":7108165,
+#             "thread_id":3135
+#          }
+#       ]
+#    }
+#
 ##
 { 'command': 'query-cpus', 'returns': ['CpuInfo'] }
 
@@ -1055,6 +1437,22 @@
 # Returns: a list of @IOThreadInfo for each iothread
 #
 # Since: 2.0
+#
+# Example:
+#
+# -> { "execute": "query-iothreads" }
+# <- { "return": [
+#          {
+#             "id":"iothread0",
+#             "thread-id":3134
+#          },
+#          {
+#             "id":"iothread1",
+#             "thread-id":3135
+#          }
+#       ]
+#    }
+#
 ##
 { 'command': 'query-iothreads', 'returns': ['IOThreadInfo'] }
 
@@ -1237,6 +1635,26 @@
 # Returns: @VncInfo
 #
 # Since: 0.14.0
+#
+# Example:
+#
+# -> { "execute": "query-vnc" }
+# <- { "return": {
+#          "enabled":true,
+#          "host":"0.0.0.0",
+#          "service":"50402",
+#          "auth":"vnc",
+#          "family":"ipv4",
+#          "clients":[
+#             {
+#                "host":"127.0.0.1",
+#                "service":"50401",
+#                "family":"ipv4"
+#             }
+#          ]
+#       }
+#    }
+#
 ##
 { 'command': 'query-vnc', 'returns': 'VncInfo' }
 
@@ -1334,7 +1752,7 @@
 # @enabled: true if the SPICE server is enabled, false otherwise
 #
 # @migrated: true if the last guest migration completed and spice
-#            migration had completed as well. false otherwise.
+#            migration had completed as well. false otherwise. (since 1.4)
 #
 # @host: #optional The hostname the SPICE server is bound to.  This depends on
 #        the name resolution on the host and may be an IP address.
@@ -1352,9 +1770,7 @@
 #
 # @mouse-mode: The mode in which the mouse cursor is displayed currently. Can
 #              be determined by the client or the server, or unknown if spice
-#              server doesn't provide this information.
-#
-#              Since: 1.1
+#              server doesn't provide this information. (since: 1.1)
 #
 # @channels: a list of @SpiceChannel for each active spice channel
 #
@@ -1373,6 +1789,40 @@
 # Returns: @SpiceInfo
 #
 # Since: 0.14.0
+#
+# Example:
+#
+# -> { "execute": "query-spice" }
+# <- { "return": {
+#          "enabled": true,
+#          "auth": "spice",
+#          "port": 5920,
+#          "tls-port": 5921,
+#          "host": "0.0.0.0",
+#          "channels": [
+#             {
+#                "port": "54924",
+#                "family": "ipv4",
+#                "channel-type": 1,
+#                "connection-id": 1804289383,
+#                "host": "127.0.0.1",
+#                "channel-id": 0,
+#                "tls": true
+#             },
+#             {
+#                "port": "36710",
+#                "family": "ipv4",
+#                "channel-type": 4,
+#                "connection-id": 1804289383,
+#                "host": "127.0.0.1",
+#                "channel-id": 0,
+#                "tls": false
+#             },
+#             [ ... more channels follow ... ]
+#          ]
+#       }
+#    }
+#
 ##
 { 'command': 'query-spice', 'returns': 'SpiceInfo' }
 
@@ -1394,11 +1844,22 @@
 # Return information about the balloon device.
 #
 # Returns: @BalloonInfo on success
+#
 #          If the balloon driver is enabled but not functional because the KVM
 #          kernel module cannot support it, KvmMissingCap
+#
 #          If no balloon device is present, DeviceNotActive
 #
 # Since: 0.14.0
+#
+# Example:
+#
+# -> { "execute": "query-balloon" }
+# <- { "return": {
+#          "actual": 1073741824,
+#       }
+#    }
+#
 ##
 { 'command': 'query-balloon', 'returns': 'BalloonInfo' }
 
@@ -1425,6 +1886,8 @@
 # @type: 'io' if the region is a PIO region
 #        'memory' if the region is a MMIO region
 #
+# @size: memory size
+#
 # @prefetch: #optional if @type is 'memory', true if the memory is prefetchable
 #
 # @mem_type_64: #optional if @type is 'memory', true if the BAR is 64-bit
@@ -1558,9 +2021,144 @@
 #
 # Return information about the PCI bus topology of the guest.
 #
-# Returns: a list of @PciInfo for each PCI bus
+# Returns: a list of @PciInfo for each PCI bus. Each bus is
+# represented by a json-object, which has a key with a json-array of
+# all PCI devices attached to it. Each device is represented by a
+# json-object.
 #
 # Since: 0.14.0
+#
+# Example:
+#
+# -> { "execute": "query-pci" }
+# <- { "return": [
+#          {
+#             "bus": 0,
+#             "devices": [
+#                {
+#                   "bus": 0,
+#                   "qdev_id": "",
+#                   "slot": 0,
+#                   "class_info": {
+#                      "class": 1536,
+#                      "desc": "Host bridge"
+#                   },
+#                   "id": {
+#                      "device": 32902,
+#                      "vendor": 4663
+#                   },
+#                   "function": 0,
+#                   "regions": [
+#                   ]
+#                },
+#                {
+#                   "bus": 0,
+#                   "qdev_id": "",
+#                   "slot": 1,
+#                   "class_info": {
+#                      "class": 1537,
+#                      "desc": "ISA bridge"
+#                   },
+#                   "id": {
+#                      "device": 32902,
+#                      "vendor": 28672
+#                   },
+#                   "function": 0,
+#                   "regions": [
+#                   ]
+#                },
+#                {
+#                   "bus": 0,
+#                   "qdev_id": "",
+#                   "slot": 1,
+#                   "class_info": {
+#                      "class": 257,
+#                      "desc": "IDE controller"
+#                   },
+#                   "id": {
+#                      "device": 32902,
+#                      "vendor": 28688
+#                   },
+#                   "function": 1,
+#                   "regions": [
+#                      {
+#                         "bar": 4,
+#                         "size": 16,
+#                         "address": 49152,
+#                         "type": "io"
+#                      }
+#                   ]
+#                },
+#                {
+#                   "bus": 0,
+#                   "qdev_id": "",
+#                   "slot": 2,
+#                   "class_info": {
+#                      "class": 768,
+#                      "desc": "VGA controller"
+#                   },
+#                   "id": {
+#                      "device": 4115,
+#                      "vendor": 184
+#                   },
+#                   "function": 0,
+#                   "regions": [
+#                      {
+#                         "prefetch": true,
+#                         "mem_type_64": false,
+#                         "bar": 0,
+#                         "size": 33554432,
+#                         "address": 4026531840,
+#                         "type": "memory"
+#                      },
+#                      {
+#                         "prefetch": false,
+#                         "mem_type_64": false,
+#                         "bar": 1,
+#                         "size": 4096,
+#                         "address": 4060086272,
+#                         "type": "memory"
+#                      },
+#                      {
+#                         "prefetch": false,
+#                         "mem_type_64": false,
+#                         "bar": 6,
+#                         "size": 65536,
+#                         "address": -1,
+#                         "type": "memory"
+#                      }
+#                   ]
+#                },
+#                {
+#                   "bus": 0,
+#                   "qdev_id": "",
+#                   "irq": 11,
+#                   "slot": 4,
+#                   "class_info": {
+#                      "class": 1280,
+#                      "desc": "RAM controller"
+#                   },
+#                   "id": {
+#                      "device": 6900,
+#                      "vendor": 4098
+#                   },
+#                   "function": 0,
+#                   "regions": [
+#                      {
+#                         "bar": 0,
+#                         "size": 32,
+#                         "address": 49280,
+#                         "type": "io"
+#                      }
+#                   ]
+#                }
+#             ]
+#          }
+#       ]
+#    }
+#
+# Note: This example has been shortened as the real response is too long.
+#
 ##
 { 'command': 'query-pci', 'returns': ['PciInfo'] }
 
@@ -1573,6 +2171,11 @@
 # unexpected.
 #
 # Since: 0.14.0
+#
+# Example:
+#
+# -> { "execute": "quit" }
+# <- { "return": {} }
 ##
 { 'command': 'quit' }
 
@@ -1587,6 +2190,12 @@
 #         state.  In "inmigrate" state, it will ensure that the guest
 #         remains paused once migration finishes, as if the -S option was
 #         passed on the command line.
+#
+# Example:
+#
+# -> { "execute": "stop" }
+# <- { "return": {} }
+#
 ##
 { 'command': 'stop' }
 
@@ -1596,6 +2205,12 @@
 # Performs a hard reset of a guest.
 #
 # Since: 0.14.0
+#
+# Example:
+#
+# -> { "execute": "system_reset" }
+# <- { "return": {} }
+#
 ##
 { 'command': 'system_reset' }
 
@@ -1610,6 +2225,11 @@
 #        returning does not indicate that a guest has accepted the request or
 #        that it has shut down.  Many guests will respond to this command by
 #        prompting the user in some way.
+# Example:
+#
+# -> { "execute": "system_powerdown" }
+# <- { "return": {} }
+#
 ##
 { 'command': 'system_powerdown' }
 
@@ -1634,6 +2254,12 @@
 # Returns: Nothing on success
 #
 # Since: 1.5
+#
+# Example:
+#
+# -> { "execute": "cpu-add", "arguments": { "id": 2 } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'cpu-add', 'data': {'id': 'int'} }
 
@@ -1656,6 +2282,15 @@
 # Since: 0.14.0
 #
 # Notes: Errors were not reliably returned until 1.1
+#
+# Example:
+#
+# -> { "execute": "memsave",
+#      "arguments": { "val": 10,
+#                     "size": 100,
+#                     "filename": "/tmp/virtual-mem-dump" } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'memsave',
   'data': {'val': 'int', 'size': 'int', 'filename': 'str', '*cpu-index': 'int'} }
@@ -1676,6 +2311,15 @@
 # Since: 0.14.0
 #
 # Notes: Errors were not reliably returned until 1.1
+#
+# Example:
+#
+# -> { "execute": "pmemsave",
+#      "arguments": { "val": 10,
+#                     "size": 100,
+#                     "filename": "/tmp/physical-mem-dump" } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'pmemsave',
   'data': {'val': 'int', 'size': 'int', 'filename': 'str'} }
@@ -1696,6 +2340,12 @@
 #         this case, the effect of the command is to make sure the guest
 #         starts once migration finishes, removing the effect of the -S
 #         command line option if it was passed.
+#
+# Example:
+#
+# -> { "execute": "cont" }
+# <- { "return": {} }
+#
 ##
 { 'command': 'cont' }
 
@@ -1707,6 +2357,12 @@
 # Since:  1.1
 #
 # Returns:  nothing.
+#
+# Example:
+#
+# -> { "execute": "system_wakeup" }
+# <- { "return": {} }
+#
 ##
 { 'command': 'system_wakeup' }
 
@@ -1714,12 +2370,19 @@
 # @inject-nmi:
 #
 # Injects a Non-Maskable Interrupt into the default CPU (x86/s390) or all CPUs (ppc64).
+# The command fails when the guest doesn't support injecting.
 #
 # Returns:  If successful, nothing
 #
 # Since:  0.14.0
 #
 # Note: prior to 2.1, this command was only supported for x86 and s390 VMs
+#
+# Example:
+#
+# -> { "execute": "inject-nmi" }
+# <- { "return": {} }
+#
 ##
 { 'command': 'inject-nmi' }
 
@@ -1740,6 +2403,13 @@
 # Notes: Not all network adapters support setting link status.  This command
 #        will succeed even if the network adapter does not support link status
 #        notification.
+#
+# Example:
+#
+# -> { "execute": "set_link",
+#      "arguments": { "name": "e1000.0", "up": false } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'set_link', 'data': {'name': 'str', 'up': 'bool'} }
 
@@ -1760,6 +2430,12 @@
 #        size independent of this command.
 #
 # Since: 0.14.0
+#
+# Example:
+#
+# -> { "execute": "balloon", "arguments": { "value": 536870912 } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'balloon', 'data': {'value': 'int'} }
 
@@ -1846,6 +2522,28 @@
 # operation fails, then the entire set of actions will be abandoned and the
 # appropriate error returned.
 #
+# For external snapshots, the dictionary contains the device, the file to use for
+# the new snapshot, and the format.  The default format, if not specified, is
+# qcow2.
+#
+# Each new snapshot defaults to being created by QEMU (wiping any
+# contents if the file already exists), but it is also possible to reuse
+# an externally-created file.  In the latter case, you should ensure that
+# the new image file has the same contents as the current one; QEMU cannot
+# perform any meaningful check.  Typically this is achieved by using the
+# current image file as the backing file for the new image.
+#
+# On failure, the original disks pre-snapshot attempt will be used.
+#
+# For internal snapshots, the dictionary contains the device and the snapshot's
+# name.  If an internal snapshot matching name already exists, the request will
+# be rejected.  Only some image formats support it, for example, qcow2, rbd,
+# and sheepdog.
+#
+# On failure, qemu will try delete the newly created internal snapshot in the
+# transaction.  When an I/O error occurs during deletion, the user needs to fix
+# it later with qemu-img or other command.
+#
 # @actions: List of @TransactionAction;
 #           information needed for the respective operations.
 #
@@ -1854,6 +2552,7 @@
 #              for additional detail.
 #
 # Returns: nothing on success
+#
 #          Errors depend on the operations of the transaction
 #
 # Note: The transaction aborts on the first failure.  Therefore, there will be
@@ -1861,6 +2560,28 @@
 # subsequent actions will not have been attempted.
 #
 # Since: 1.1
+#
+# Example:
+#
+# -> { "execute": "transaction",
+#      "arguments": { "actions": [
+#          { "type": "blockdev-snapshot-sync", "data" : { "device": "ide-hd0",
+#                                      "snapshot-file": "/some/place/my-image",
+#                                      "format": "qcow2" } },
+#          { "type": "blockdev-snapshot-sync", "data" : { "node-name": "myfile",
+#                                      "snapshot-file": "/some/place/my-image2",
+#                                      "snapshot-node-name": "node3432",
+#                                      "mode": "existing",
+#                                      "format": "qcow2" } },
+#          { "type": "blockdev-snapshot-sync", "data" : { "device": "ide-hd1",
+#                                      "snapshot-file": "/some/place/my-image2",
+#                                      "mode": "existing",
+#                                      "format": "qcow2" } },
+#          { "type": "blockdev-snapshot-internal-sync", "data" : {
+#                                      "device": "ide-hd2",
+#                                      "name": "snapshot0" } } ] } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'transaction',
   'data': { 'actions': [ 'TransactionAction' ],
@@ -1882,15 +2603,26 @@
 # Since: 0.14.0
 #
 # Notes: This command only exists as a stop-gap.  Its use is highly
-#        discouraged.  The semantics of this command are not guaranteed.
+#        discouraged.  The semantics of this command are not
+#        guaranteed: this means that command names, arguments and
+#        responses can change or be removed at ANY time.  Applications
+#        that rely on long term stability guarantees should NOT
+#        use this command.
 #
 #        Known limitations:
 #
 #        o This command is stateless, this means that commands that depend
 #          on state information (such as getfd) might not work
 #
-#       o Commands that prompt the user for data (eg. 'cont' when the block
-#         device is encrypted) don't currently work
+#        o Commands that prompt the user for data (eg. 'cont' when the block
+#          device is encrypted) don't currently work
+#
+# Example:
+#
+# -> { "execute": "human-monitor-command",
+#      "arguments": { "command-line": "info kvm" } }
+# <- { "return": "kvm support: enabled\r\n" }
+#
 ##
 { 'command': 'human-monitor-command',
   'data': {'command-line': 'str', '*cpu-index': 'int'},
@@ -1906,6 +2638,12 @@
 # Notes: This command succeeds even if there is no migration process running.
 #
 # Since: 0.14.0
+#
+# Example:
+#
+# -> { "execute": "migrate_cancel" }
+# <- { "return": {} }
+#
 ##
 { 'command': 'migrate_cancel' }
 
@@ -1921,6 +2659,12 @@
 # Notes: This command is deprecated in favor of 'migrate-set-parameters'
 #
 # Since: 0.14.0
+#
+# Example:
+#
+# -> { "execute": "migrate_set_downtime", "arguments": { "value": 0.1 } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'migrate_set_downtime', 'data': {'value': 'number'} }
 
@@ -1929,20 +2673,26 @@
 #
 # Set maximum speed for migration.
 #
-# @value: maximum speed in bytes.
+# @value: maximum speed in bytes per second.
 #
 # Returns: nothing on success
 #
 # Notes: This command is deprecated in favor of 'migrate-set-parameters'
 #
 # Since: 0.14.0
+#
+# Example:
+#
+# -> { "execute": "migrate_set_speed", "arguments": { "value": 1024 } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'migrate_set_speed', 'data': {'value': 'int'} }
 
 ##
 # @migrate-set-cache-size:
 #
-# Set XBZRLE cache size
+# Set cache size to be used by XBZRLE migration
 #
 # @value: cache size in bytes
 #
@@ -1952,17 +2702,30 @@
 # Returns: nothing on success
 #
 # Since: 1.2
+#
+# Example:
+#
+# -> { "execute": "migrate-set-cache-size",
+#      "arguments": { "value": 536870912 } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'migrate-set-cache-size', 'data': {'value': 'int'} }
 
 ##
 # @query-migrate-cache-size:
 #
-# query XBZRLE cache size
+# Query migration XBZRLE cache size
 #
 # Returns: XBZRLE cache size in bytes
 #
 # Since: 1.2
+#
+# Example:
+#
+# -> { "execute": "query-migrate-cache-size" }
+# <- { "return": 67108864 }
+#
 ##
 { 'command': 'query-migrate-cache-size', 'returns': 'int' }
 
@@ -2079,6 +2842,13 @@
 #          If Spice is not enabled, DeviceNotFound
 #
 # Since: 0.14.0
+#
+# Example:
+#
+# -> { "execute": "set_password", "arguments": { "protocol": "vnc",
+#                                                "password": "secret" } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'set_password',
   'data': {'protocol': 'str', 'password': 'str', '*connected': 'str'} }
@@ -2105,6 +2875,13 @@
 #        coordinate server time with client time.  It is not recommended to
 #        use the absolute time version of the @time parameter unless you're
 #        sure you are on the same machine as the QEMU instance.
+#
+# Example:
+#
+# -> { "execute": "expire_password", "arguments": { "protocol": "vnc",
+#                                                   "time": "+60" } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'expire_password', 'data': {'protocol': 'str', 'time': 'str'} }
 
@@ -2155,6 +2932,23 @@
 #         change-vnc-password.
 #
 # Since: 0.14.0
+#
+# Example:
+#
+# 1. Change a removable medium
+#
+# -> { "execute": "change",
+#      "arguments": { "device": "ide1-cd0",
+#                     "target": "/srv/images/Fedora-12-x86_64-DVD.iso" } }
+# <- { "return": {} }
+#
+# 2. Change VNC password
+#
+# -> { "execute": "change",
+#      "arguments": { "device": "vnc", "target": "password",
+#                     "arg": "foobar1" } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'change',
   'data': {'device': 'str', 'target': 'str', '*arg': 'str'} }
@@ -2237,6 +3031,22 @@
 # Returns: nothing on success
 #
 # Since: 0.14.0
+#
+# Notes:
+#
+# 1. The 'query-migrate' command should be used to check migration's progress
+#    and final result (this information is provided by the 'status' member)
+#
+# 2. All boolean arguments default to false
+#
+# 3. The user Monitor's "detach" argument is invalid in QMP and should not
+#    be used
+#
+# Example:
+#
+# -> { "execute": "migrate", "arguments": { "uri": "tcp:0:4446" } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'migrate',
   'data': {'uri': 'str', '*blk': 'bool', '*inc': 'bool', '*detach': 'bool' } }
@@ -2253,9 +3063,24 @@
 # Returns: nothing on success
 #
 # Since: 2.3
-# Note: It's a bad idea to use a string for the uri, but it needs to stay
-# compatible with -incoming and the format of the uri is already exposed
-# above libvirt
+#
+# Notes:
+#
+# 1. It's a bad idea to use a string for the uri, but it needs to stay
+#    compatible with -incoming and the format of the uri is already exposed
+#    above libvirt.
+#
+# 2. QEMU must be started with -incoming defer to allow migrate-incoming to
+#    be used.
+#
+# 3. The uri format is the same as for -incoming
+#
+# Example:
+#
+# -> { "execute": "migrate-incoming",
+#      "arguments": { "uri": "tcp::4446" } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'migrate-incoming', 'data': {'uri': 'str' } }
 
@@ -2272,6 +3097,13 @@
 # Returns: Nothing on success
 #
 # Since: 1.1
+#
+# Example:
+#
+# -> { "execute": "xen-save-devices-state",
+#      "arguments": { "filename": "/tmp/save" } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'xen-save-devices-state', 'data': {'filename': 'str'} }
 
@@ -2285,12 +3117,21 @@
 # Returns: nothing
 #
 # Since: 1.3
+#
+# Example:
+#
+# -> { "execute": "xen-set-global-dirty-log",
+#      "arguments": { "enable": true } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'xen-set-global-dirty-log', 'data': { 'enable': 'bool' } }
 
 ##
 # @device_add:
 #
+# Add a device.
+#
 # @driver: the name of the new device's driver
 #
 # @bus: #optional the device's parent bus (device tree path)
@@ -2299,8 +3140,6 @@
 #
 # Additional arguments depend on the type.
 #
-# Add a device.
-#
 # Notes:
 # 1. For detailed information about this command, please refer to the
 #    'docs/qdev-device-use.txt' file.
@@ -2333,7 +3172,7 @@
 #
 # Remove a device from a guest
 #
-# @id: the name or QOM path of the device
+# @id: the device's ID or QOM path
 #
 # Returns: Nothing on success
 #          If @id is not a valid device, DeviceNotFound
@@ -2346,6 +3185,17 @@
 #        for all devices.
 #
 # Since: 0.14.0
+#
+# Example:
+#
+# -> { "execute": "device_del",
+#      "arguments": { "id": "net1" } }
+# <- { "return": {} }
+#
+# -> { "execute": "device_del",
+#      "arguments": { "id": "/machine/peripheral-anon/device[0]" } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'device_del', 'data': {'id': 'str'} }
 
@@ -2412,9 +3262,18 @@
 #          @length is not allowed to be specified with non-elf @format at the
 #          same time (since 2.0)
 #
+# Note: All boolean arguments default to false
+#
 # Returns: nothing on success
 #
 # Since: 1.2
+#
+# Example:
+#
+# -> { "execute": "dump-guest-memory",
+#      "arguments": { "protocol": "fd:dump" } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'dump-guest-memory',
   'data': { 'paging': 'bool', 'protocol': 'str', '*detach': 'bool',
@@ -2465,6 +3324,13 @@
 # Returns: A @DumpStatus object showing the dump status.
 #
 # Since: 2.6
+#
+# Example:
+#
+# -> { "execute": "query-dump" }
+# <- { "return": { "status": "active", "completed": 1024000,
+#                  "total": 2048000 } }
+#
 ##
 { 'command': 'query-dump', 'returns': 'DumpQueryResult' }
 
@@ -2488,6 +3354,13 @@
 #           dump-guest-memory
 #
 # Since: 2.0
+#
+# Example:
+#
+# -> { "execute": "query-dump-guest-memory-capability" }
+# <- { "return": { "formats":
+#                  ["elf", "kdump-zlib", "kdump-lzo", "kdump-snappy"] }
+#
 ##
 { 'command': 'query-dump-guest-memory-capability',
   'returns': 'DumpGuestMemoryCapability' }
@@ -2502,6 +3375,13 @@
 # This command is only supported on s390 architecture.
 #
 # Since: 2.5
+#
+# Example:
+#
+# -> { "execute": "dump-skeys",
+#      "arguments": { "filename": "/tmp/skeys" } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'dump-skeys',
   'data': { 'filename': 'str' } }
@@ -2527,6 +3407,14 @@
 #
 # Returns: Nothing on success
 #          If @type is not a valid network backend, DeviceNotFound
+#
+# Example:
+#
+# -> { "execute": "netdev_add",
+#      "arguments": { "type": "user", "id": "netdev1",
+#                     "dnssearch": "example.org" } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'netdev_add',
   'data': {'type': 'str', 'id': 'str'},
@@ -2543,6 +3431,12 @@
 #          If @id is not a valid network backend, DeviceNotFound
 #
 # Since: 0.14.0
+#
+# Example:
+#
+# -> { "execute": "netdev_del", "arguments": { "id": "netdev1" } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'netdev_del', 'data': {'id': 'str'} }
 
@@ -2561,6 +3455,14 @@
 #          Error if @qom-type is not a valid class name
 #
 # Since: 2.0
+#
+# Example:
+#
+# -> { "execute": "object-add",
+#      "arguments": { "qom-type": "rng-random", "id": "rng1",
+#                     "props": { "filename": "/dev/hwrng" } } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'object-add',
   'data': {'qom-type': 'str', 'id': 'str', '*props': 'any'} }
@@ -2576,6 +3478,12 @@
 #          Error if @id is not a valid id for a QOM object
 #
 # Since: 2.0
+#
+# Example:
+#
+# -> { "execute": "object-del", "arguments": { "id": "rng1" } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'object-del', 'data': {'id': 'str'} }
 
@@ -3150,8 +4058,15 @@
 # Notes: If @fdname already exists, the file descriptor assigned to
 #        it will be closed and replaced by the received file
 #        descriptor.
+#
 #        The 'closefd' command can be used to explicitly close the
 #        file descriptor when it is no longer needed.
+#
+# Example:
+#
+# -> { "execute": "getfd", "arguments": { "fdname": "fd1" } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'getfd', 'data': {'fdname': 'str'} }
 
@@ -3165,6 +4080,12 @@
 # Returns: Nothing on success
 #
 # Since: 0.14.0
+#
+# Example:
+#
+# -> { "execute": "closefd", "arguments": { "fdname": "fd1" } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'closefd', 'data': {'fdname': 'str'} }
 
@@ -3514,7 +4435,9 @@
 # @opaque: #optional A free-form string that can be used to describe the fd.
 #
 # Returns: @AddfdInfo on success
+#
 #          If file descriptor was not received, FdNotSupplied
+#
 #          If @fdset-id is a negative value, InvalidParameterValue
 #
 # Notes: The list of fd sets is shared by all monitor connections.
@@ -3522,6 +4445,12 @@
 #        If @fdset-id is not specified, a new fd set will be created.
 #
 # Since: 1.2.0
+#
+# Example:
+#
+# -> { "execute": "add-fd", "arguments": { "fdset-id": 1 } }
+# <- { "return": { "fdset-id": 1, "fd": 3 } }
+#
 ##
 { 'command': 'add-fd', 'data': {'*fdset-id': 'int', '*opaque': 'str'},
   'returns': 'AddfdInfo' }
@@ -3544,6 +4473,12 @@
 #
 #        If @fd is not specified, all file descriptors in @fdset-id
 #        will be removed.
+#
+# Example:
+#
+# -> { "execute": "remove-fd", "arguments": { "fdset-id": 1, "fd": 3 } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'remove-fd', 'data': {'fdset-id': 'int', '*fd': 'int'} }
 
@@ -3586,6 +4521,37 @@
 #
 # Note: The list of fd sets is shared by all monitor connections.
 #
+# Example:
+#
+# -> { "execute": "query-fdsets" }
+# <- { "return": [
+#        {
+#          "fds": [
+#            {
+#              "fd": 30,
+#              "opaque": "rdonly:/path/to/file"
+#            },
+#            {
+#              "fd": 24,
+#              "opaque": "rdwr:/path/to/file"
+#            }
+#          ],
+#          "fdset-id": 1
+#        },
+#        {
+#          "fds": [
+#            {
+#              "fd": 28
+#            },
+#            {
+#              "fd": 29
+#            }
+#          ],
+#          "fdset-id": 0
+#        }
+#      ]
+#    }
+#
 ##
 { 'command': 'query-fdsets', 'returns': ['FdsetInfo'] }
 
@@ -3678,6 +4644,14 @@
 #
 # Since: 1.3.0
 #
+# Example:
+#
+# -> { "execute": "send-key",
+#      "arguments": { "keys": [ { "type": "qcode", "data": "ctrl" },
+#                               { "type": "qcode", "data": "alt" },
+#                               { "type": "qcode", "data": "delete" } ] } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'send-key',
   'data': { 'keys': ['KeyValue'], '*hold-time': 'int' } }
@@ -3692,6 +4666,13 @@
 # Returns: Nothing on success
 #
 # Since: 0.14.0
+#
+# Example:
+#
+# -> { "execute": "screendump",
+#      "arguments": { "filename": "/tmp/image" } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'screendump', 'data': {'filename': 'str'} }
 
@@ -3916,6 +4897,25 @@
 # Returns: ChardevReturn.
 #
 # Since: 1.4
+#
+# Example:
+#
+# -> { "execute" : "chardev-add",
+#      "arguments" : { "id" : "foo",
+#                      "backend" : { "type" : "null", "data" : {} } } }
+# <- { "return": {} }
+#
+# -> { "execute" : "chardev-add",
+#      "arguments" : { "id" : "bar",
+#                      "backend" : { "type" : "file",
+#                                    "data" : { "out" : "/tmp/bar.log" } } } }
+# <- { "return": {} }
+#
+# -> { "execute" : "chardev-add",
+#      "arguments" : { "id" : "baz",
+#                      "backend" : { "type" : "pty", "data" : {} } } }
+# <- { "return": { "pty" : "/dev/pty/42" } }
+#
 ##
 { 'command': 'chardev-add', 'data': {'id'      : 'str',
                                      'backend' : 'ChardevBackend' },
@@ -3931,6 +4931,12 @@
 # Returns: Nothing on success
 #
 # Since: 1.4
+#
+# Example:
+#
+# -> { "execute": "chardev-remove", "arguments": { "id" : "foo" } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'chardev-remove', 'data': {'id': 'str'} }
 
@@ -3953,6 +4959,12 @@
 # Returns: a list of TpmModel
 #
 # Since: 1.5
+#
+# Example:
+#
+# -> { "execute": "query-tpm-models" }
+# <- { "return": [ "tpm-tis" ] }
+#
 ##
 { 'command': 'query-tpm-models', 'returns': ['TpmModel'] }
 
@@ -3975,6 +4987,12 @@
 # Returns: a list of TpmType
 #
 # Since: 1.5
+#
+# Example:
+#
+# -> { "execute": "query-tpm-types" }
+# <- { "return": [ "passthrough" ] }
+#
 ##
 { 'command': 'query-tpm-types', 'returns': ['TpmType'] }
 
@@ -4031,6 +5049,25 @@
 # Returns: @TPMInfo on success
 #
 # Since: 1.5
+#
+# Example:
+#
+# -> { "execute": "query-tpm" }
+# <- { "return":
+#      [
+#        { "model": "tpm-tis",
+#          "options":
+#            { "type": "passthrough",
+#              "data":
+#                { "cancel-path": "/sys/class/misc/tpm0/device/cancel",
+#                  "path": "/dev/tpm0"
+#                }
+#            },
+#          "id": "tpm0"
+#        }
+#      ]
+#    }
+#
 ##
 { 'command': 'query-tpm', 'returns': ['TPMInfo'] }
 
@@ -4157,6 +5194,28 @@
 #          @option).  Returns an error if the given @option doesn't exist.
 #
 # Since: 1.5
+#
+# Example:
+#
+# -> { "execute": "query-command-line-options",
+#      "arguments": { "option": "option-rom" } }
+# <- { "return": [
+#         {
+#             "parameters": [
+#                 {
+#                     "name": "romfile",
+#                     "type": "string"
+#                 },
+#                 {
+#                     "name": "bootindex",
+#                     "type": "number"
+#                 }
+#             ],
+#             "option": "option-rom"
+#         }
+#      ]
+#    }
+#
 ##
 {'command': 'query-command-line-options', 'data': { '*option': 'str' },
  'returns': ['CommandLineOptionInfo'] }
@@ -4278,6 +5337,36 @@
 #          isn't a NIC.
 #
 # Since: 1.6
+#
+# Example:
+#
+# -> { "execute": "query-rx-filter", "arguments": { "name": "vnet0" } }
+# <- { "return": [
+#         {
+#             "promiscuous": true,
+#             "name": "vnet0",
+#             "main-mac": "52:54:00:12:34:56",
+#             "unicast": "normal",
+#             "vlan": "normal",
+#             "vlan-table": [
+#                 4,
+#                 0
+#             ],
+#             "unicast-table": [
+#             ],
+#             "multicast": "normal",
+#             "multicast-overflow": false,
+#             "unicast-overflow": false,
+#             "multicast-table": [
+#                 "01:00:5e:00:00:01",
+#                 "33:33:00:00:00:01",
+#                 "33:33:ff:12:34:56"
+#             ],
+#             "broadcast-allowed": false
+#         }
+#       ]
+#    }
+#
 ##
 { 'command': 'query-rx-filter', 'data': { '*name': 'str' },
   'returns': ['RxFilterInfo'] }
@@ -4376,9 +5465,9 @@
 #
 # Returns: Nothing on success.
 #
-# The @display and @head parameters can be used to send the input
-# event to specific input devices in case (a) multiple input devices
-# of the same kind are added to the virtual machine and (b) you have
+# The @device and @head parameters can be used to send the input event
+# to specific input devices in case (a) multiple input devices of the
+# same kind are added to the virtual machine and (b) you have
 # configured input routing (see docs/multiseat.txt) for those input
 # devices.  The parameters work exactly like the device and head
 # properties of input devices.  If @device is missing, only devices
@@ -4388,6 +5477,48 @@
 # precedence.
 #
 # Since: 2.6
+#
+# Note: The consoles are visible in the qom tree, under
+# /backend/console[$index]. They have a device link and head property,
+# so it is possible to map which console belongs to which device and
+# display.
+#
+# Example:
+#
+# 1. Press left mouse button.
+#
+# -> { "execute": "input-send-event",
+#     "arguments": { "device": "video0",
+#                    "events": [ { "type": "btn",
+#                    "data" : { "down": true, "button": "left" } } ] } }
+# <- { "return": {} }
+#
+# -> { "execute": "input-send-event",
+#     "arguments": { "device": "video0",
+#                    "events": [ { "type": "btn",
+#                    "data" : { "down": false, "button": "left" } } ] } }
+# <- { "return": {} }
+#
+# 2. Press ctrl-alt-del.
+#
+# -> { "execute": "input-send-event",
+#      "arguments": { "events": [
+#         { "type": "key", "data" : { "down": true,
+#           "key": {"type": "qcode", "data": "ctrl" } } },
+#         { "type": "key", "data" : { "down": true,
+#           "key": {"type": "qcode", "data": "alt" } } },
+#         { "type": "key", "data" : { "down": true,
+#           "key": {"type": "qcode", "data": "delete" } } } ] } }
+# <- { "return": {} }
+#
+# 3. Move mouse pointer to absolute coordinates (20000, 400).
+#
+# -> { "execute": "input-send-event" ,
+#   "arguments": { "events": [
+#                { "type": "abs", "data" : { "axis": "x", "value" : 20000 } },
+#                { "type": "abs", "data" : { "axis": "y", "value" : 400 } } ] } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'input-send-event',
   'data': { '*device': 'str',
@@ -4487,6 +5618,30 @@
 # Returns: a list of @Memdev.
 #
 # Since: 2.1
+#
+# Example:
+#
+# -> { "execute": "query-memdev" }
+# <- { "return": [
+#        {
+#          "size": 536870912,
+#          "merge": false,
+#          "dump": true,
+#          "prealloc": false,
+#          "host-nodes": [0, 1],
+#          "policy": "bind"
+#        },
+#        {
+#          "size": 536870912,
+#          "merge": false,
+#          "dump": true,
+#          "prealloc": true,
+#          "host-nodes": [2, 3],
+#          "policy": "preferred"
+#        }
+#      ]
+#    }
+#
 ##
 { 'command': 'query-memdev', 'returns': ['Memdev'] }
 
@@ -4540,6 +5695,22 @@
 # Lists available memory devices and their state
 #
 # Since: 2.1
+#
+# Example:
+#
+# -> { "execute": "query-memory-devices" }
+# <- { "return": [ { "data":
+#                       { "addr": 5368709120,
+#                         "hotpluggable": true,
+#                         "hotplugged": true,
+#                         "id": "d1",
+#                         "memdev": "/objects/memX",
+#                         "node": 0,
+#                         "size": 1073741824,
+#                         "slot": 0},
+#                    "type": "dimm"
+#                  } ] }
+#
 ##
 { 'command': 'query-memory-devices', 'returns': ['MemoryDeviceInfo'] }
 
@@ -4580,10 +5751,20 @@
 ##
 # @query-acpi-ospm-status:
 #
-# Lists ACPI OSPM status of ACPI device objects,
-# which might be reported via _OST method
+# Return a list of ACPIOSTInfo for devices that support status
+# reporting via ACPI _OST method.
 #
 # Since: 2.1
+#
+# Example:
+#
+# -> { "execute": "query-acpi-ospm-status" }
+# <- { "return": [ { "device": "d1", "slot": "0", "slot-type": "DIMM", "source": 1, "status": 0},
+#                  { "slot": "1", "slot-type": "DIMM", "source": 0, "status": 0},
+#                  { "slot": "2", "slot-type": "DIMM", "source": 0, "status": 0},
+#                  { "slot": "3", "slot-type": "DIMM", "source": 0, "status": 0}
+#    ]}
+#
 ##
 { 'command': 'query-acpi-ospm-status', 'returns': ['ACPIOSTInfo'] }
 
@@ -4650,6 +5831,12 @@
 # command.
 #
 # Since: 2.1
+#
+# Example:
+#
+# -> { "execute": "rtc-reset-reinjection" }
+# <- { "return": {} }
+#
 ##
 { 'command': 'rtc-reset-reinjection' }
 
@@ -4685,6 +5872,13 @@
 # format.
 #
 # Since: 2.7
+#
+# Example:
+#
+# -> { "execute": "xen-load-devices-state",
+#      "arguments": { "filename": "/tmp/resume" } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'xen-load-devices-state', 'data': {'filename': 'str'} }
 
@@ -4721,6 +5915,13 @@
 # Returns: a list of GICCapability objects.
 #
 # Since: 2.6
+#
+# Example:
+#
+# -> { "execute": "query-gic-capabilities" }
+# <- { "return": [{ "version": 2, "emulated": true, "kernel": false },
+#                 { "version": 3, "emulated": false, "kernel": true } ] }
+#
 ##
 { 'command': 'query-gic-capabilities', 'returns': ['GICCapability'] }
 
@@ -4777,5 +5978,33 @@
 # Returns: a list of HotpluggableCPU objects.
 #
 # Since: 2.7
+#
+# Example:
+#
+# For pseries machine type started with -smp 2,cores=2,maxcpus=4 -cpu POWER8:
+#
+# -> { "execute": "query-hotpluggable-cpus" }
+# <- {"return": [
+#      { "props": { "core": 8 }, "type": "POWER8-spapr-cpu-core",
+#        "vcpus-count": 1 },
+#      { "props": { "core": 0 }, "type": "POWER8-spapr-cpu-core",
+#        "vcpus-count": 1, "qom-path": "/machine/unattached/device[0]"}
+#    ]}'
+#
+# For pc machine type started with -smp 1,maxcpus=2:
+#
+# -> { "execute": "query-hotpluggable-cpus" }
+# <- {"return": [
+#      {
+#         "type": "qemu64-x86_64-cpu", "vcpus-count": 1,
+#         "props": {"core-id": 0, "socket-id": 1, "thread-id": 0}
+#      },
+#      {
+#         "qom-path": "/machine/unattached/device[0]",
+#         "type": "qemu64-x86_64-cpu", "vcpus-count": 1,
+#         "props": {"core-id": 0, "socket-id": 0, "thread-id": 0}
+#      }
+#    ]}
+#
 ##
 { 'command': 'query-hotpluggable-cpus', 'returns': ['HotpluggableCPU'] }
diff --git a/qapi/block-core.json b/qapi/block-core.json
index 05cedc3..688f556 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -469,6 +469,87 @@
 # Returns: a list of @BlockInfo describing each virtual block device
 #
 # Since: 0.14.0
+#
+# Example:
+#
+# -> { "execute": "query-block" }
+# <- {
+#       "return":[
+#          {
+#             "io-status": "ok",
+#             "device":"ide0-hd0",
+#             "locked":false,
+#             "removable":false,
+#             "inserted":{
+#                "ro":false,
+#                "drv":"qcow2",
+#                "encrypted":false,
+#                "file":"disks/test.qcow2",
+#                "backing_file_depth":1,
+#                "bps":1000000,
+#                "bps_rd":0,
+#                "bps_wr":0,
+#                "iops":1000000,
+#                "iops_rd":0,
+#                "iops_wr":0,
+#                "bps_max": 8000000,
+#                "bps_rd_max": 0,
+#                "bps_wr_max": 0,
+#                "iops_max": 0,
+#                "iops_rd_max": 0,
+#                "iops_wr_max": 0,
+#                "iops_size": 0,
+#                "detect_zeroes": "on",
+#                "write_threshold": 0,
+#                "image":{
+#                   "filename":"disks/test.qcow2",
+#                   "format":"qcow2",
+#                   "virtual-size":2048000,
+#                   "backing_file":"base.qcow2",
+#                   "full-backing-filename":"disks/base.qcow2",
+#                   "backing-filename-format":"qcow2",
+#                   "snapshots":[
+#                      {
+#                         "id": "1",
+#                         "name": "snapshot1",
+#                         "vm-state-size": 0,
+#                         "date-sec": 10000200,
+#                         "date-nsec": 12,
+#                         "vm-clock-sec": 206,
+#                         "vm-clock-nsec": 30
+#                      }
+#                   ],
+#                   "backing-image":{
+#                       "filename":"disks/base.qcow2",
+#                       "format":"qcow2",
+#                       "virtual-size":2048000
+#                   }
+#                }
+#             },
+#             "type":"unknown"
+#          },
+#          {
+#             "io-status": "ok",
+#             "device":"ide1-cd0",
+#             "locked":false,
+#             "removable":true,
+#             "type":"unknown"
+#          },
+#          {
+#             "device":"floppy0",
+#             "locked":false,
+#             "removable":true,
+#             "type":"unknown"
+#          },
+#          {
+#             "device":"sd0",
+#             "locked":false,
+#             "removable":true,
+#             "type":"unknown"
+#          }
+#       ]
+#    }
+#
 ##
 { 'command': 'query-block', 'returns': ['BlockInfo'] }
 
@@ -616,6 +697,9 @@
 # @stats:  A @BlockDeviceStats for the device.
 #
 # @parent: #optional This describes the file block device if it has one.
+#          Contains recursively the statistics of the underlying
+#          protocol (e.g. the host file for a qcow2 image). If there is
+#          no underlying protocol, this field is omitted
 #
 # @backing: #optional This describes the backing block device if it has one.
 #           (Since 2.0)
@@ -643,6 +727,106 @@
 # Returns: A list of @BlockStats for each virtual block devices.
 #
 # Since: 0.14.0
+#
+# Example:
+#
+# -> { "execute": "query-blockstats" }
+# <- {
+#       "return":[
+#          {
+#             "device":"ide0-hd0",
+#             "parent":{
+#                "stats":{
+#                   "wr_highest_offset":3686448128,
+#                   "wr_bytes":9786368,
+#                   "wr_operations":751,
+#                   "rd_bytes":122567168,
+#                   "rd_operations":36772
+#                   "wr_total_times_ns":313253456
+#                   "rd_total_times_ns":3465673657
+#                   "flush_total_times_ns":49653
+#                   "flush_operations":61,
+#                   "rd_merged":0,
+#                   "wr_merged":0,
+#                   "idle_time_ns":2953431879,
+#                   "account_invalid":true,
+#                   "account_failed":false
+#                }
+#             },
+#             "stats":{
+#                "wr_highest_offset":2821110784,
+#                "wr_bytes":9786368,
+#                "wr_operations":692,
+#                "rd_bytes":122739200,
+#                "rd_operations":36604
+#                "flush_operations":51,
+#                "wr_total_times_ns":313253456
+#                "rd_total_times_ns":3465673657
+#                "flush_total_times_ns":49653,
+#                "rd_merged":0,
+#                "wr_merged":0,
+#                "idle_time_ns":2953431879,
+#                "account_invalid":true,
+#                "account_failed":false
+#             }
+#          },
+#          {
+#             "device":"ide1-cd0",
+#             "stats":{
+#                "wr_highest_offset":0,
+#                "wr_bytes":0,
+#                "wr_operations":0,
+#                "rd_bytes":0,
+#                "rd_operations":0
+#                "flush_operations":0,
+#                "wr_total_times_ns":0
+#                "rd_total_times_ns":0
+#                "flush_total_times_ns":0,
+#                "rd_merged":0,
+#                "wr_merged":0,
+#                "account_invalid":false,
+#                "account_failed":false
+#             }
+#          },
+#          {
+#             "device":"floppy0",
+#             "stats":{
+#                "wr_highest_offset":0,
+#                "wr_bytes":0,
+#                "wr_operations":0,
+#                "rd_bytes":0,
+#                "rd_operations":0
+#                "flush_operations":0,
+#                "wr_total_times_ns":0
+#                "rd_total_times_ns":0
+#                "flush_total_times_ns":0,
+#                "rd_merged":0,
+#                "wr_merged":0,
+#                "account_invalid":false,
+#                "account_failed":false
+#             }
+#          },
+#          {
+#             "device":"sd0",
+#             "stats":{
+#                "wr_highest_offset":0,
+#                "wr_bytes":0,
+#                "wr_operations":0,
+#                "rd_bytes":0,
+#                "rd_operations":0
+#                "flush_operations":0,
+#                "wr_total_times_ns":0
+#                "rd_total_times_ns":0
+#                "flush_total_times_ns":0,
+#                "rd_merged":0,
+#                "wr_merged":0,
+#                "account_invalid":false,
+#                "account_failed":false
+#             }
+#          }
+#       ]
+#    }
+#
 ##
 { 'command': 'query-blockstats',
   'data': { '*query-nodes': 'bool' },
@@ -787,6 +971,13 @@
 #         occur if an invalid password is specified.
 #
 # Since: 0.14.0
+#
+# Example:
+#
+# -> { "execute": "block_passwd", "arguments": { "device": "ide0-hd0",
+#                                                "password": "12345" } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'block_passwd', 'data': {'*device': 'str',
                                       '*node-name': 'str', 'password': 'str'} }
@@ -808,6 +999,13 @@
 #          If @device is not a valid block device, DeviceNotFound
 #
 # Since: 0.14.0
+#
+# Example:
+#
+# -> { "execute": "block_resize",
+#      "arguments": { "device": "scratch", "size": 1073741824 } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'block_resize', 'data': { '*device': 'str',
                                        '*node-name': 'str',
@@ -839,7 +1037,9 @@
 #
 # @node-name: #optional graph node name to generate the snapshot from (Since 2.0)
 #
-# @snapshot-file: the target of the new image. A new file will be created.
+# @snapshot-file: the target of the new image. If the file exists, or
+# if it is a device, the snapshot will be created in the existing
+# file/device. Otherwise, a new file will be created.
 #
 # @snapshot-node-name: #optional the graph node name of the new image (Since 2.0)
 #
@@ -973,6 +1173,16 @@
 #          If @device is not a valid block device, DeviceNotFound
 #
 # Since: 0.14.0
+#
+# Example:
+#
+# -> { "execute": "blockdev-snapshot-sync",
+#      "arguments": { "device": "ide-hd0",
+#                     "snapshot-file":
+#                     "/some/place/my-image",
+#                     "format": "qcow2" } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'blockdev-snapshot-sync',
   'data': 'BlockdevSnapshotSync' }
@@ -983,9 +1193,31 @@
 #
 # Generates a snapshot of a block device.
 #
+# Create a snapshot, by installing 'node' as the backing image of
+# 'overlay'. Additionally, if 'node' is associated with a block
+# device, the block device changes to using 'overlay' as its new active
+# image.
+#
 # For the arguments, see the documentation of BlockdevSnapshot.
 #
 # Since: 2.5
+#
+# Example:
+#
+# -> { "execute": "blockdev-add",
+#      "arguments": { "options": { "driver": "qcow2",
+#                                  "node-name": "node1534",
+#                                  "file": { "driver": "file",
+#                                            "filename": "hd1.qcow2" },
+#                                  "backing": "" } } }
+#
+# <- { "return": {} }
+#
+# -> { "execute": "blockdev-snapshot",
+#      "arguments": { "node": "ide-hd0",
+#                     "overlay": "node1534" } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'blockdev-snapshot',
   'data': 'BlockdevSnapshot' }
@@ -1001,7 +1233,9 @@
 # updated.
 #
 # @image-node-name: The name of the block driver state node of the
-#                   image to modify.
+#                   image to modify. The "device" argument is used
+#                   to verify "image-node-name" is in the chain
+#                   described by "device".
 #
 # @device:          The device name or node-name of the root node that owns
 #                   image-node-name.
@@ -1011,6 +1245,10 @@
 #                   when specifying the string or the image chain may
 #                   not be able to be reopened again.
 #
+# Returns: Nothing on success
+#
+#          If "device" does not exist or cannot be determined, DeviceNotFound
+#
 # Since: 2.1
 ##
 { 'command': 'change-backing-file',
@@ -1029,7 +1267,7 @@
 # @device:  the device name or node-name of a root node
 #
 # @base:   #optional The file name of the backing image to write data into.
-#                    If not specified, this is the deepest backing image
+#                    If not specified, this is the deepest backing image.
 #
 # @top:    #optional The file name of the backing image within the image chain,
 #                    which contains the topmost data to be committed down. If
@@ -1075,6 +1313,13 @@
 #
 # Since: 1.3
 #
+# Example:
+#
+# -> { "execute": "block-commit",
+#      "arguments": { "device": "virtio0",
+#                     "top": "/tmp/snap1.qcow2" } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'block-commit',
   'data': { '*job-id': 'str', 'device': 'str', '*base': 'str', '*top': 'str',
@@ -1095,6 +1340,15 @@
 #          If @device is not a valid block device, GenericError
 #
 # Since: 1.6
+#
+# Example:
+#
+# -> { "execute": "drive-backup",
+#      "arguments": { "device": "drive0",
+#                     "sync": "full",
+#                     "target": "backup.img" } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'drive-backup', 'boxed': true,
   'data': 'DriveBackup' }
@@ -1114,6 +1368,14 @@
 #          If @device is not a valid block device, DeviceNotFound
 #
 # Since: 2.3
+#
+# Example:
+# -> { "execute": "blockdev-backup",
+#      "arguments": { "device": "src-id",
+#                     "sync": "full",
+#                     "target": "tgt-id" } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'blockdev-backup', 'boxed': true,
   'data': 'BlockdevBackup' }
@@ -1127,13 +1389,67 @@
 # Returns: the list of BlockDeviceInfo
 #
 # Since: 2.0
+#
+# Example:
+#
+# -> { "execute": "query-named-block-nodes" }
+# <- { "return": [ { "ro":false,
+#                    "drv":"qcow2",
+#                    "encrypted":false,
+#                    "file":"disks/test.qcow2",
+#                    "node-name": "my-node",
+#                    "backing_file_depth":1,
+#                    "bps":1000000,
+#                    "bps_rd":0,
+#                    "bps_wr":0,
+#                    "iops":1000000,
+#                    "iops_rd":0,
+#                    "iops_wr":0,
+#                    "bps_max": 8000000,
+#                    "bps_rd_max": 0,
+#                    "bps_wr_max": 0,
+#                    "iops_max": 0,
+#                    "iops_rd_max": 0,
+#                    "iops_wr_max": 0,
+#                    "iops_size": 0,
+#                    "write_threshold": 0,
+#                    "image":{
+#                       "filename":"disks/test.qcow2",
+#                       "format":"qcow2",
+#                       "virtual-size":2048000,
+#                       "backing_file":"base.qcow2",
+#                       "full-backing-filename":"disks/base.qcow2",
+#                       "backing-filename-format":"qcow2",
+#                       "snapshots":[
+#                          {
+#                             "id": "1",
+#                             "name": "snapshot1",
+#                             "vm-state-size": 0,
+#                             "date-sec": 10000200,
+#                             "date-nsec": 12,
+#                             "vm-clock-sec": 206,
+#                             "vm-clock-nsec": 30
+#                          }
+#                       ],
+#                       "backing-image":{
+#                           "filename":"disks/base.qcow2",
+#                           "format":"qcow2",
+#                           "virtual-size":2048000
+#                       }
+#                    } } ] }
+#
 ##
 { 'command': 'query-named-block-nodes', 'returns': [ 'BlockDeviceInfo' ] }
 
 ##
 # @drive-mirror:
 #
-# Start mirroring a block device's writes to a new destination.
+# Start mirroring a block device's writes to a new destination. target
+# specifies the target of the new image. If the file exists, or if it
+# is a device, it will be used as the new destination for writes. If
+# it does not exist, a new file will be created. format specifies the
+# format of the mirror image, default is to probe if mode='existing',
+# else the format of the source.
 #
 # See DriveMirror for parameter descriptions
 #
@@ -1141,6 +1457,16 @@
 #          If @device is not a valid block device, GenericError
 #
 # Since: 1.3
+#
+# Example:
+#
+# -> { "execute": "drive-mirror",
+#      "arguments": { "device": "ide-hd0",
+#                     "target": "/some/place/my-image",
+#                     "sync": "full",
+#                     "format": "qcow2" } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'drive-mirror', 'boxed': true,
   'data': 'DriveMirror' }
@@ -1241,13 +1567,20 @@
 ##
 # @block-dirty-bitmap-add:
 #
-# Create a dirty bitmap with a name on the node
+# Create a dirty bitmap with a name on the node, and start tracking the writes.
 #
 # Returns: nothing on success
 #          If @node is not a valid block device or node, DeviceNotFound
 #          If @name is already taken, GenericError with an explanation
 #
 # Since: 2.4
+#
+# Example:
+#
+# -> { "execute": "block-dirty-bitmap-add",
+#      "arguments": { "node": "drive0", "name": "bitmap0" } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'block-dirty-bitmap-add',
   'data': 'BlockDirtyBitmapAdd' }
@@ -1255,7 +1588,8 @@
 ##
 # @block-dirty-bitmap-remove:
 #
-# Remove a dirty bitmap on the node
+# Stop write tracking and remove the dirty bitmap that was created
+# with block-dirty-bitmap-add.
 #
 # Returns: nothing on success
 #          If @node is not a valid block device or node, DeviceNotFound
@@ -1263,6 +1597,13 @@
 #          if @name is frozen by an operation, GenericError
 #
 # Since: 2.4
+#
+# Example:
+#
+# -> { "execute": "block-dirty-bitmap-remove",
+#      "arguments": { "node": "drive0", "name": "bitmap0" } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'block-dirty-bitmap-remove',
   'data': 'BlockDirtyBitmap' }
@@ -1270,13 +1611,22 @@
 ##
 # @block-dirty-bitmap-clear:
 #
-# Clear (reset) a dirty bitmap on the device
+# Clear (reset) a dirty bitmap on the device, so that an incremental
+# backup from this point in time forward will only backup clusters
+# modified after this clear operation.
 #
 # Returns: nothing on success
 #          If @node is not a valid block device, DeviceNotFound
 #          If @name is not found, GenericError with an explanation
 #
 # Since: 2.4
+#
+# Example:
+#
+# -> { "execute": "block-dirty-bitmap-clear",
+#      "arguments": { "node": "drive0", "name": "bitmap0" } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'block-dirty-bitmap-clear',
   'data': 'BlockDirtyBitmap' }
@@ -1324,6 +1674,15 @@
 # Returns: nothing on success.
 #
 # Since: 2.6
+#
+# Example:
+#
+# -> { "execute": "blockdev-mirror",
+#      "arguments": { "device": "ide-hd0",
+#                     "target": "target0",
+#                     "sync": "full" } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'blockdev-mirror',
   'data': { '*job-id': 'str', 'device': 'str', 'target': 'str',
@@ -1365,6 +1724,26 @@
 #          If @device is not a valid block device, DeviceNotFound
 #
 # Since: 1.1
+#
+# Example:
+#
+# -> { "execute": "block_set_io_throttle",
+#      "arguments": { "id": "ide0-1-0",
+#                     "bps": 1000000,
+#                     "bps_rd": 0,
+#                     "bps_wr": 0,
+#                     "iops": 0,
+#                     "iops_rd": 0,
+#                     "iops_wr": 0,
+#                     "bps_max": 8000000,
+#                     "bps_rd_max": 0,
+#                     "bps_wr_max": 0,
+#                     "iops_max": 0,
+#                     "iops_rd_max": 0,
+#                     "iops_wr_max": 0,
+#                     "bps_max_length": 60,
+#                     "iops_size": 0 } }
+# <- { "return": {} }
 ##
 { 'command': 'block_set_io_throttle', 'boxed': true,
   'data': 'BlockIOThrottle' }
@@ -1513,7 +1892,17 @@
 #            'stop' and 'enospc' can only be used if the block device
 #            supports io-status (see BlockInfo).  Since 1.3.
 #
+# Returns: Nothing on success. If @device does not exist, DeviceNotFound.
+#
 # Since: 1.1
+#
+# Example:
+#
+# -> { "execute": "block-stream",
+#      "arguments": { "device": "virtio0",
+#                     "base": "/tmp/master.qcow2" } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'block-stream',
   'data': { '*job-id': 'str', 'device': 'str', '*base': 'str',
@@ -2442,13 +2831,52 @@
 # BlockBackend will be created; otherwise, @node-name is mandatory at the top
 # level and no BlockBackend will be created.
 #
-# This command is still a work in progress.  It doesn't support all
+# Note: This command is still a work in progress.  It doesn't support all
 # block drivers among other things.  Stay away from it unless you want
 # to help with its development.
 #
 # For the arguments, see the documentation of BlockdevOptions.
 #
 # Since: 1.7
+#
+# Example:
+#
+# 1.
+# -> { "execute": "blockdev-add",
+#      "arguments": {
+#          "options" : { "driver": "qcow2",
+#                        "file": { "driver": "file",
+#                                  "filename": "test.qcow2" } } } }
+# <- { "return": {} }
+#
+# 2.
+# -> { "execute": "blockdev-add",
+#      "arguments": {
+#          "options": {
+#            "driver": "qcow2",
+#            "node-name": "node0",
+#            "discard": "unmap",
+#            "cache": {
+#                "direct": true,
+#                "writeback": true
+#            },
+#            "file": {
+#                "driver": "file",
+#                "filename": "/tmp/test.qcow2"
+#            },
+#            "backing": {
+#                "driver": "raw",
+#                "file": {
+#                    "driver": "file",
+#                    "filename": "/dev/fdset/4"
+#                }
+#            }
+#          }
+#        }
+#      }
+#
+# <- { "return": {} }
+#
 ##
 { 'command': 'blockdev-add', 'data': 'BlockdevOptions', 'boxed': true }
 
@@ -2459,13 +2887,35 @@
 # The command will fail if the node is attached to a device or is
 # otherwise being used.
 #
-# This command is still a work in progress and is considered
+# Note: This command is still a work in progress and is considered
 # experimental. Stay away from it unless you want to help with its
 # development.
 #
 # @node-name: Name of the graph node to delete.
 #
 # Since: 2.5
+#
+# Example:
+#
+# -> { "execute": "blockdev-add",
+#      "arguments": {
+#          "options": {
+#              "driver": "qcow2",
+#              "node-name": "node0",
+#              "file": {
+#                  "driver": "file",
+#                  "filename": "test.qcow2"
+#              }
+#          }
+#      }
+#    }
+# <- { "return": {} }
+#
+# -> { "execute": "x-blockdev-del",
+#      "arguments": { "node-name": "node0" }
+#    }
+# <- { "return": {} }
+#
 ##
 { 'command': 'x-blockdev-del', 'data': { 'node-name': 'str' } }
 
@@ -2497,6 +2947,21 @@
 #          it is locked
 #
 # Since: 2.5
+#
+# Example:
+#
+# -> { "execute": "blockdev-open-tray",
+#      "arguments": { "id": "ide0-1-0" } }
+#
+# <- { "timestamp": { "seconds": 1418751016,
+#                     "microseconds": 716996 },
+#      "event": "DEVICE_TRAY_MOVED",
+#      "data": { "device": "ide1-cd0",
+#                "id": "ide0-1-0",
+#                "tray-open": true } }
+#
+# <- { "return": {} }
+#
 ##
 { 'command': 'blockdev-open-tray',
   'data': { '*device': 'str',
@@ -2517,6 +2982,21 @@
 # @id:      #optional The name or QOM path of the guest device (since: 2.8)
 #
 # Since: 2.5
+#
+# Example:
+#
+# -> { "execute": "blockdev-close-tray",
+#      "arguments": { "id": "ide0-1-0" } }
+#
+# <- { "timestamp": { "seconds": 1418751345,
+#                     "microseconds": 272147 },
+#      "event": "DEVICE_TRAY_MOVED",
+#      "data": { "device": "ide1-cd0",
+#                "id": "ide0-1-0",
+#                "tray-open": false } }
+#
+# <- { "return": {} }
+#
 ##
 { 'command': 'blockdev-close-tray',
   'data': { '*device': 'str',
@@ -2539,6 +3019,32 @@
 # @id:     #optional The name or QOM path of the guest device (since: 2.8)
 #
 # Since: 2.5
+#
+# Example:
+#
+# -> { "execute": "x-blockdev-remove-medium",
+#      "arguments": { "id": "ide0-1-0" } }
+#
+# <- { "error": { "class": "GenericError",
+#                 "desc": "Tray of device 'ide0-1-0' is not open" } }
+#
+# -> { "execute": "blockdev-open-tray",
+#      "arguments": { "id": "ide0-1-0" } }
+#
+# <- { "timestamp": { "seconds": 1418751627,
+#                     "microseconds": 549958 },
+#      "event": "DEVICE_TRAY_MOVED",
+#      "data": { "device": "ide1-cd0",
+#                "id": "ide0-1-0",
+#                "tray-open": true } }
+#
+# <- { "return": {} }
+#
+# -> { "execute": "x-blockdev-remove-medium",
+#      "arguments": { "device": "ide0-1-0" } }
+#
+# <- { "return": {} }
+#
 ##
 { 'command': 'x-blockdev-remove-medium',
   'data': { '*device': 'str',
@@ -2551,8 +3057,9 @@
 # device's tray must currently be open (unless there is no attached guest
 # device) and there must be no medium inserted already.
 #
-# This command is still a work in progress and is considered experimental.
-# Stay away from it unless you want to help with its development.
+# Note: This command is still a work in progress and is considered
+# experimental.  Stay away from it unless you want to help with its
+# development.
 #
 # @device:    #optional Block device name (deprecated, use @id instead)
 #
@@ -2561,6 +3068,23 @@
 # @node-name: name of a node in the block driver state graph
 #
 # Since: 2.5
+#
+# Example:
+#
+# -> { "execute": "blockdev-add",
+#      "arguments": {
+#          "options": { "node-name": "node0",
+#                       "driver": "raw",
+#                       "file": { "driver": "file",
+#                                 "filename": "fedora.iso" } } } }
+# <- { "return": {} }
+#
+# -> { "execute": "x-blockdev-insert-medium",
+#      "arguments": { "id": "ide0-1-0",
+#                     "node-name": "node0" } }
+#
+# <- { "return": {} }
+#
 ##
 { 'command': 'x-blockdev-insert-medium',
   'data': { '*device': 'str',
@@ -2581,6 +3105,7 @@
 # @read-write:  Makes the device writable
 #
 # Since: 2.3
+#
 ##
 { 'enum': 'BlockdevChangeReadOnlyMode',
   'data': ['retain', 'read-only', 'read-write'] }
@@ -2608,6 +3133,37 @@
 #                   to 'retain'
 #
 # Since: 2.5
+#
+# Examples:
+#
+# 1. Change a removable medium
+#
+# -> { "execute": "blockdev-change-medium",
+#      "arguments": { "id": "ide0-1-0",
+#                     "filename": "/srv/images/Fedora-12-x86_64-DVD.iso",
+#                     "format": "raw" } }
+# <- { "return": {} }
+#
+# 2. Load a read-only medium into a writable drive
+#
+# -> { "execute": "blockdev-change-medium",
+#      "arguments": { "id": "floppyA",
+#                     "filename": "/srv/images/ro.img",
+#                     "format": "raw",
+#                     "read-only-mode": "retain" } }
+#
+# <- { "error":
+#      { "class": "GenericError",
+#        "desc": "Could not open '/srv/images/ro.img': Permission denied" } }
+#
+# -> { "execute": "blockdev-change-medium",
+#      "arguments": { "id": "floppyA",
+#                     "filename": "/srv/images/ro.img",
+#                     "format": "raw",
+#                     "read-only-mode": "read-only" } }
+#
+# <- { "return": {} }
+#
 ##
 { 'command': 'blockdev-change-medium',
   'data': { '*device': 'str',
@@ -2637,7 +3193,10 @@
 ##
 # @BLOCK_IMAGE_CORRUPTED:
 #
-# Emitted when a corruption has been detected in a disk image
+# Emitted when a disk image is being marked corrupt. The image can be
+# identified by its device or node name. The 'device' field is always
+# present for compatibility reasons, but it can be empty ("") if the
+# image does not have a device name associated.
 #
 # @device: device name. This is always present for compatibility
 #          reasons, but it can be empty ("") if the image does not
@@ -2655,10 +3214,21 @@
 # @size: #optional, if the corruption resulted from an image access, this is
 #        the access size
 #
-# fatal: if set, the image is marked corrupt and therefore unusable after this
+# @fatal: if set, the image is marked corrupt and therefore unusable after this
 #        event and must be repaired (Since 2.2; before, every
 #        BLOCK_IMAGE_CORRUPTED event was fatal)
 #
+# Note: If action is "stop", a STOP event will eventually follow the
+#       BLOCK_IO_ERROR event.
+#
+# Example:
+#
+# <- { "event": "BLOCK_IMAGE_CORRUPTED",
+#      "data": { "device": "ide0-hd0", "node-name": "node0",
+#                "msg": "Prevented active L1 table overwrite", "offset": 196608,
+#                "size": 65536 },
+#      "timestamp": { "seconds": 1378126126, "microseconds": 966463 } }
+#
 # Since: 1.7
 ##
 { 'event': 'BLOCK_IMAGE_CORRUPTED',
@@ -2699,6 +3269,16 @@
 # BLOCK_IO_ERROR event
 #
 # Since: 0.13.0
+#
+# Example:
+#
+# <- { "event": "BLOCK_IO_ERROR",
+#      "data": { "device": "ide0-hd1",
+#                "node-name": "#block212",
+#                "operation": "write",
+#                "action": "stop" },
+#      "timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
+#
 ##
 { 'event': 'BLOCK_IO_ERROR',
   'data': { 'device': 'str', 'node-name': 'str', 'operation': 'IoOperationType',
@@ -2728,6 +3308,15 @@
 #         interpret the error string
 #
 # Since: 1.1
+#
+# Example:
+#
+# <- { "event": "BLOCK_JOB_COMPLETED",
+#      "data": { "type": "stream", "device": "virtio-disk0",
+#                "len": 10737418240, "offset": 10737418240,
+#                "speed": 0 },
+#      "timestamp": { "seconds": 1267061043, "microseconds": 959568 } }
+#
 ##
 { 'event': 'BLOCK_JOB_COMPLETED',
   'data': { 'type'  : 'BlockJobType',
@@ -2755,6 +3344,15 @@
 # @speed: rate limit, bytes per second
 #
 # Since: 1.1
+#
+# Example:
+#
+# <- { "event": "BLOCK_JOB_CANCELLED",
+#      "data": { "type": "stream", "device": "virtio-disk0",
+#                "len": 10737418240, "offset": 134217728,
+#                "speed": 0 },
+#      "timestamp": { "seconds": 1267061043, "microseconds": 959568 } }
+#
 ##
 { 'event': 'BLOCK_JOB_CANCELLED',
   'data': { 'type'  : 'BlockJobType',
@@ -2776,6 +3374,15 @@
 # @action: action that has been taken
 #
 # Since: 1.3
+#
+# Example:
+#
+# <- { "event": "BLOCK_JOB_ERROR",
+#      "data": { "device": "ide0-hd1",
+#                "operation": "write",
+#                "action": "stop" },
+#      "timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
+#
 ##
 { 'event': 'BLOCK_JOB_ERROR',
   'data': { 'device'   : 'str',
@@ -2803,6 +3410,14 @@
 # event
 #
 # Since: 1.3
+#
+# Example:
+#
+# <- { "event": "BLOCK_JOB_READY",
+#      "data": { "device": "drive0", "type": "mirror", "speed": 0,
+#                "len": 2097152, "offset": 2097152 }
+#      "timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
+#
 ##
 { 'event': 'BLOCK_JOB_READY',
   'data': { 'type'  : 'BlockJobType',
@@ -2855,8 +3470,12 @@
 ##
 # @block-set-write-threshold:
 #
-# Change the write threshold for a block drive. An event will be delivered
-# if a write to this block drive crosses the configured threshold.
+# Change the write threshold for a block drive. An event will be
+# delivered if a write to this block drive crosses the configured
+# threshold.  The threshold is an offset, thus must be
+# non-negative. Default is no write threshold. Setting the threshold
+# to zero disables it.
+#
 # This is useful to transparently resize thin-provisioned drives without
 # the guest OS noticing.
 #
@@ -2866,6 +3485,14 @@
 #                   Use 0 to disable the threshold.
 #
 # Since: 2.3
+#
+# Example:
+#
+# -> { "execute": "block-set-write-threshold",
+#      "arguments": { "node-name": "mydev",
+#                     "write-threshold": 17179869184 } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'block-set-write-threshold',
   'data': { 'node-name': 'str', 'write-threshold': 'uint64' } }
@@ -2896,6 +3523,28 @@
 # the rest of the array.
 #
 # Since: 2.7
+#
+# Example:
+#
+# 1. Add a new node to a quorum
+# -> { "execute": "blockdev-add",
+#      "arguments": {
+#          "options": { "driver": "raw",
+#                       "node-name": "new_node",
+#                        "file": { "driver": "file",
+#                                  "filename": "test.raw" } } } }
+# <- { "return": {} }
+# -> { "execute": "x-blockdev-change",
+#      "arguments": { "parent": "disk1",
+#                     "node": "new_node" } }
+# <- { "return": {} }
+#
+# 2. Delete a quorum's node
+# -> { "execute": "x-blockdev-change",
+#      "arguments": { "parent": "disk1",
+#                     "child": "children.1" } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'x-blockdev-change',
   'data' : { 'parent': 'str',
diff --git a/qapi/block.json b/qapi/block.json
index e4ad74b..2a2d95a 100644
--- a/qapi/block.json
+++ b/qapi/block.json
@@ -81,19 +81,33 @@
 ##
 # @blockdev-snapshot-internal-sync:
 #
-# Synchronously take an internal snapshot of a block device, when the format
-# of the image used supports it.
+# Synchronously take an internal snapshot of a block device, when the
+# format of the image used supports it. If the name is an empty
+# string, or a snapshot with name already exists, the operation will
+# fail.
 #
 # For the arguments, see the documentation of BlockdevSnapshotInternal.
 #
 # Returns: nothing on success
+#
 #          If @device is not a valid block device, GenericError
+#
 #          If any snapshot matching @name exists, or @name is empty,
 #          GenericError
+#
 #          If the format of the image used does not support it,
 #          BlockFormatFeatureNotSupported
 #
 # Since: 1.7
+#
+# Example:
+#
+# -> { "execute": "blockdev-snapshot-internal-sync",
+#      "arguments": { "device": "ide-hd0",
+#                     "name": "snapshot0" }
+#    }
+# <- { "return": {} }
+#
 ##
 { 'command': 'blockdev-snapshot-internal-sync',
   'data': 'BlockdevSnapshotInternal' }
@@ -121,6 +135,24 @@
 #          If @id and @name are both not specified, GenericError
 #
 # Since: 1.7
+#
+# Example:
+#
+# -> { "execute": "blockdev-snapshot-delete-internal-sync",
+#      "arguments": { "device": "ide-hd0",
+#                     "name": "snapshot0" }
+#    }
+# <- { "return": {
+#                    "id": "1",
+#                    "name": "snapshot0",
+#                    "vm-state-size": 0,
+#                    "date-sec": 1000012,
+#                    "date-nsec": 10,
+#                    "vm-clock-sec": 100,
+#                    "vm-clock-nsec": 20
+#      }
+#    }
+#
 ##
 { 'command': 'blockdev-snapshot-delete-internal-sync',
   'data': { 'device': 'str', '*id': 'str', '*name': 'str'},
@@ -135,15 +167,21 @@
 #
 # @id:      #optional The name or QOM path of the guest device (since: 2.8)
 #
-# @force:   @optional If true, eject regardless of whether the drive is locked.
+# @force:   #optional If true, eject regardless of whether the drive is locked.
 #           If not specified, the default value is false.
 #
 # Returns:  Nothing on success
+#
 #           If @device is not a valid block device, DeviceNotFound
 #
-# Notes:    Ejecting a device will no media results in success
+# Notes:    Ejecting a device with no media results in success
 #
 # Since: 0.14.0
+#
+# Example:
+#
+# -> { "execute": "eject", "arguments": { "device": "ide1-0-1" } }
+# <- { "return": {} }
 ##
 { 'command': 'eject',
   'data': { '*device': 'str',
@@ -210,6 +248,16 @@
 # @tray-open: true if the tray has been opened or false if it has been closed
 #
 # Since: 1.1
+#
+# Example:
+#
+# <- { "event": "DEVICE_TRAY_MOVED",
+#      "data": { "device": "ide1-cd0",
+#                "id": "/machine/unattached/device[22]",
+#                "tray-open": true
+#      },
+#      "timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
+#
 ##
 { 'event': 'DEVICE_TRAY_MOVED',
   'data': { 'device': 'str', 'id': 'str', 'tray-open': 'bool' } }
diff --git a/qapi/common.json b/qapi/common.json
index d93f159..b626647 100644
--- a/qapi/common.json
+++ b/qapi/common.json
@@ -77,6 +77,21 @@
 # Returns:  A @VersionInfo object describing the current version of QEMU.
 #
 # Since: 0.14.0
+#
+# Example:
+#
+# -> { "execute": "query-version" }
+# <- {
+#       "return":{
+#          "qemu":{
+#             "major":0,
+#             "minor":11,
+#             "micro":5
+#          },
+#          "package":""
+#       }
+#    }
+#
 ##
 { 'command': 'query-version', 'returns': 'VersionInfo' }
 
@@ -99,6 +114,23 @@
 # Returns: A list of @CommandInfo for all supported commands
 #
 # Since: 0.14.0
+#
+# Example:
+#
+# -> { "execute": "query-commands" }
+# <- {
+#      "return":[
+#         {
+#            "name":"query-balloon"
+#         },
+#         {
+#            "name":"system_powerdown"
+#         }
+#      ]
+#    }
+#
+# Note: This example has been shortened as the real response is too long.
+#
 ##
 { 'command': 'query-commands', 'returns': ['CommandInfo'] }
 
diff --git a/qapi/event.json b/qapi/event.json
index 59942b0..512b4be 100644
--- a/qapi/event.json
+++ b/qapi/event.json
@@ -14,6 +14,12 @@
 # not exit, and a STOP event will eventually follow the SHUTDOWN event
 #
 # Since: 0.12.0
+#
+# Example:
+#
+# <- { "event": "SHUTDOWN",
+#      "timestamp": { "seconds": 1267040730, "microseconds": 682951 } }
+#
 ##
 { 'event': 'SHUTDOWN' }
 
@@ -24,6 +30,12 @@
 # system, such as via ACPI.
 #
 # Since: 0.12.0
+#
+# Example:
+#
+# <- { "event": "POWERDOWN",
+#      "timestamp": { "seconds": 1267040730, "microseconds": 682951 } }
+#
 ##
 { 'event': 'POWERDOWN' }
 
@@ -33,6 +45,12 @@
 # Emitted when the virtual machine is reset
 #
 # Since: 0.12.0
+#
+# Example:
+#
+# <- { "event": "RESET",
+#      "timestamp": { "seconds": 1267041653, "microseconds": 9518 } }
+#
 ##
 { 'event': 'RESET' }
 
@@ -42,6 +60,12 @@
 # Emitted when the virtual machine is stopped
 #
 # Since: 0.12.0
+#
+# Example:
+#
+# <- { "event": "STOP",
+#      "timestamp": { "seconds": 1267041730, "microseconds": 281295 } }
+#
 ##
 { 'event': 'STOP' }
 
@@ -51,6 +75,12 @@
 # Emitted when the virtual machine resumes execution
 #
 # Since: 0.12.0
+#
+# Example:
+#
+# <- { "event": "RESUME",
+#      "timestamp": { "seconds": 1271770767, "microseconds": 582542 } }
+#
 ##
 { 'event': 'RESUME' }
 
@@ -61,6 +91,12 @@
 # which is sometimes called standby state
 #
 # Since: 1.1
+#
+# Example:
+#
+# <- { "event": "SUSPEND",
+#      "timestamp": { "seconds": 1344456160, "microseconds": 309119 } }
+#
 ##
 { 'event': 'SUSPEND' }
 
@@ -73,6 +109,12 @@
 # Note: QEMU shuts down (similar to event @SHUTDOWN) when entering this state
 #
 # Since: 1.2
+#
+# Example:
+#
+# <-   { "event": "SUSPEND_DISK",
+#        "timestamp": { "seconds": 1344456160, "microseconds": 309119 } }
+#
 ##
 { 'event': 'SUSPEND_DISK' }
 
@@ -82,6 +124,12 @@
 # Emitted when the guest has woken up from suspend state and is running
 #
 # Since: 1.1
+#
+# Example:
+#
+# <- { "event": "WAKEUP",
+#      "timestamp": { "seconds": 1344522075, "microseconds": 745528 } }
+#
 ##
 { 'event': 'WAKEUP' }
 
@@ -93,7 +141,16 @@
 # @offset: offset between base RTC clock (as specified by -rtc base), and
 #          new RTC clock value
 #
+# Note: This event is rate-limited.
+#
 # Since: 0.13.0
+#
+# Example:
+#
+# <-   { "event": "RTC_CHANGE",
+#        "data": { "offset": 78 },
+#        "timestamp": { "seconds": 1267020223, "microseconds": 435656 } }
+#
 ##
 { 'event': 'RTC_CHANGE',
   'data': { 'offset': 'int' } }
@@ -108,7 +165,16 @@
 # Note: If action is "reset", "shutdown", or "pause" the WATCHDOG event is
 # followed respectively by the RESET, SHUTDOWN, or STOP events
 #
+# Note: This event is rate-limited.
+#
 # Since: 0.13.0
+#
+# Example:
+#
+# <- { "event": "WATCHDOG",
+#      "data": { "action": "reset" },
+#      "timestamp": { "seconds": 1267061043, "microseconds": 959568 } }
+#
 ##
 { 'event': 'WATCHDOG',
   'data': { 'action': 'WatchdogExpirationAction' } }
@@ -125,6 +191,14 @@
 # @path: device path
 #
 # Since: 1.5
+#
+# Example:
+#
+# <- { "event": "DEVICE_DELETED",
+#      "data": { "device": "virtio-net-pci-0",
+#                "path": "/machine/peripheral/virtio-net-pci-0" },
+#      "timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
+#
 ##
 { 'event': 'DEVICE_DELETED',
   'data': { '*device': 'str', 'path': 'str' } }
@@ -140,6 +214,15 @@
 # @path: device path
 #
 # Since: 1.6
+#
+# Example:
+#
+# <- { "event": "NIC_RX_FILTER_CHANGED",
+#      "data": { "name": "vnet0",
+#                "path": "/machine/peripheral/vnet0/virtio-backend" },
+#      "timestamp": { "seconds": 1368697518, "microseconds": 326866 } }
+#    }
+#
 ##
 { 'event': 'NIC_RX_FILTER_CHANGED',
   'data': { '*name': 'str', 'path': 'str' } }
@@ -157,6 +240,17 @@
 # the authentication ID is not provided
 #
 # Since: 0.13.0
+#
+# Example:
+#
+# <- { "event": "VNC_CONNECTED",
+#      "data": {
+#            "server": { "auth": "sasl", "family": "ipv4",
+#                        "service": "5901", "host": "0.0.0.0" },
+#            "client": { "family": "ipv4", "service": "58425",
+#                        "host": "127.0.0.1" } },
+#      "timestamp": { "seconds": 1262976601, "microseconds": 975795 } }
+#
 ##
 { 'event': 'VNC_CONNECTED',
   'data': { 'server': 'VncServerInfo',
@@ -173,6 +267,17 @@
 # @client: client information
 #
 # Since: 0.13.0
+#
+# Example:
+#
+# <-  { "event": "VNC_INITIALIZED",
+#       "data": {
+#            "server": { "auth": "sasl", "family": "ipv4",
+#                        "service": "5901", "host": "0.0.0.0"},
+#            "client": { "family": "ipv4", "service": "46089",
+#                        "host": "127.0.0.1", "sasl_username": "luiz" } },
+#       "timestamp": { "seconds": 1263475302, "microseconds": 150772 } }
+#
 ##
 { 'event': 'VNC_INITIALIZED',
   'data': { 'server': 'VncServerInfo',
@@ -188,6 +293,17 @@
 # @client: client information
 #
 # Since: 0.13.0
+#
+# Example:
+#
+# <- { "event": "VNC_DISCONNECTED",
+#      "data": {
+#            "server": { "auth": "sasl", "family": "ipv4",
+#                        "service": "5901", "host": "0.0.0.0" },
+#            "client": { "family": "ipv4", "service": "58425",
+#                        "host": "127.0.0.1", "sasl_username": "luiz" } },
+#      "timestamp": { "seconds": 1262976601, "microseconds": 975795 } }
+#
 ##
 { 'event': 'VNC_DISCONNECTED',
   'data': { 'server': 'VncServerInfo',
@@ -203,6 +319,16 @@
 # @client: client information
 #
 # Since: 0.14.0
+#
+# Example:
+#
+# <- { "timestamp": {"seconds": 1290688046, "microseconds": 388707},
+#      "event": "SPICE_CONNECTED",
+#      "data": {
+#        "server": { "port": "5920", "family": "ipv4", "host": "127.0.0.1"},
+#        "client": {"port": "52873", "family": "ipv4", "host": "127.0.0.1"}
+#    }}
+#
 ##
 { 'event': 'SPICE_CONNECTED',
   'data': { 'server': 'SpiceBasicInfo',
@@ -219,6 +345,18 @@
 # @client: client information
 #
 # Since: 0.14.0
+#
+# Example:
+#
+# <- { "timestamp": {"seconds": 1290688046, "microseconds": 417172},
+#      "event": "SPICE_INITIALIZED",
+#      "data": {"server": {"auth": "spice", "port": "5921",
+#                          "family": "ipv4", "host": "127.0.0.1"},
+#               "client": {"port": "49004", "family": "ipv4", "channel-type": 3,
+#                          "connection-id": 1804289383, "host": "127.0.0.1",
+#                          "channel-id": 0, "tls": true}
+#    }}
+#
 ##
 { 'event': 'SPICE_INITIALIZED',
   'data': { 'server': 'SpiceServerInfo',
@@ -234,6 +372,16 @@
 # @client: client information
 #
 # Since: 0.14.0
+#
+# Example:
+#
+# <- { "timestamp": {"seconds": 1290688046, "microseconds": 388707},
+#      "event": "SPICE_DISCONNECTED",
+#      "data": {
+#        "server": { "port": "5920", "family": "ipv4", "host": "127.0.0.1"},
+#        "client": {"port": "52873", "family": "ipv4", "host": "127.0.0.1"}
+#    }}
+#
 ##
 { 'event': 'SPICE_DISCONNECTED',
   'data': { 'server': 'SpiceBasicInfo',
@@ -245,6 +393,12 @@
 # Emitted when SPICE migration has completed
 #
 # Since: 1.3
+#
+# Example:
+#
+# <- { "timestamp": {"seconds": 1290688046, "microseconds": 417172},
+#      "event": "SPICE_MIGRATE_COMPLETED" }
+#
 ##
 { 'event': 'SPICE_MIGRATE_COMPLETED' }
 
@@ -256,6 +410,13 @@
 # @status: @MigrationStatus describing the current migration status.
 #
 # Since: 2.4
+#
+# Example:
+#
+# <- {"timestamp": {"seconds": 1432121972, "microseconds": 744001},
+#     "event": "MIGRATION",
+#     "data": {"status": "completed"} }
+#
 ##
 { 'event': 'MIGRATION',
   'data': {'status': 'MigrationStatus'}}
@@ -269,6 +430,12 @@
 # @pass: An incrementing count (starting at 1 on the first pass)
 #
 # Since: 2.6
+#
+# Example:
+#
+# { "timestamp": {"seconds": 1449669631, "microseconds": 239225},
+#   "event": "MIGRATION_PASS", "data": {"pass": 2} }
+#
 ##
 { 'event': 'MIGRATION_PASS',
   'data': { 'pass': 'int' } }
@@ -281,6 +448,13 @@
 # Since: 2.1
 #
 # @info: ACPIOSTInfo type as described in qapi-schema.json
+#
+# Example:
+#
+# <- { "event": "ACPI_DEVICE_OST",
+#      "data": { "device": "d1", "slot": "0",
+#                "slot-type": "DIMM", "source": 1, "status": 0 } }
+#
 ##
 { 'event': 'ACPI_DEVICE_OST',
      'data': { 'info': 'ACPIOSTInfo' } }
@@ -293,7 +467,16 @@
 #
 # @actual: actual level of the guest memory balloon in bytes
 #
+# Note: this event is rate-limited.
+#
 # Since: 1.2
+#
+# Example:
+#
+# <- { "event": "BALLOON_CHANGE",
+#      "data": { "actual": 944766976 },
+#      "timestamp": { "seconds": 1267020223, "microseconds": 435656 } }
+#
 ##
 { 'event': 'BALLOON_CHANGE',
   'data': { 'actual': 'int' } }
@@ -306,6 +489,12 @@
 # @action: action that has been taken, currently always "pause"
 #
 # Since: 1.5
+#
+# Example:
+#
+# <- { "event": "GUEST_PANICKED",
+#      "data": { "action": "pause" } }
+#
 ##
 { 'event': 'GUEST_PANICKED',
   'data': { 'action': 'GuestPanicAction' } }
@@ -321,7 +510,16 @@
 #
 # @sectors-count: failed read operation sector count
 #
+# Note: This event is rate-limited.
+#
 # Since: 2.0
+#
+# Example:
+#
+# <- { "event": "QUORUM_FAILURE",
+#      "data": { "reference": "usr1", "sector-num": 345435, "sectors-count": 5 },
+#      "timestamp": { "seconds": 1344522075, "microseconds": 745528 } }
+#
 ##
 { 'event': 'QUORUM_FAILURE',
   'data': { 'reference': 'str', 'sector-num': 'int', 'sectors-count': 'int' } }
@@ -344,7 +542,26 @@
 #
 # @sectors-count: failed read operation sector count
 #
+# Note: This event is rate-limited.
+#
 # Since: 2.0
+#
+# Example:
+#
+# 1. Read operation
+#
+# { "event": "QUORUM_REPORT_BAD",
+#      "data": { "node-name": "node0", "sector-num": 345435, "sectors-count": 5,
+#                "type": "read" },
+#      "timestamp": { "seconds": 1344522075, "microseconds": 745528 } }
+#
+# 2. Flush operation
+#
+# { "event": "QUORUM_REPORT_BAD",
+#      "data": { "node-name": "node0", "sector-num": 0, "sectors-count": 2097120,
+#                "type": "flush", "error": "Broken pipe" },
+#      "timestamp": { "seconds": 1456406829, "microseconds": 291763 } }
+#
 ##
 { 'event': 'QUORUM_REPORT_BAD',
   'data': { 'type': 'QuorumOpType', '*error': 'str', 'node-name': 'str',
@@ -360,6 +577,13 @@
 # @open: true if the guest has opened the virtio-serial port
 #
 # Since: 2.1
+#
+# Example:
+#
+# <- { "event": "VSERPORT_CHANGE",
+#      "data": { "id": "channel0", "open": true },
+#      "timestamp": { "seconds": 1401385907, "microseconds": 422329 } }
+#
 ##
 { 'event': 'VSERPORT_CHANGE',
   'data': { 'id': 'str', 'open': 'bool' } }
@@ -374,6 +598,15 @@
 # @msg: Informative message
 #
 # Since: 2.4
+#
+# Example:
+#
+# <- { "event": "MEM_UNPLUG_ERROR"
+#      "data": { "device": "dimm1",
+#                "msg": "acpi: device unplug for unsupported device"
+#      },
+#      "timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
+#
 ##
 { 'event': 'MEM_UNPLUG_ERROR',
   'data': { 'device': 'str', 'msg': 'str' } }
@@ -390,6 +623,13 @@
 #         user should not try to interpret the error string.
 #
 # Since: 2.6
+#
+# Example:
+#
+# { "event": "DUMP_COMPLETED",
+#   "data": {"result": {"total": 1090650112, "status": "completed",
+#                       "completed": 1090650112} } }
+#
 ##
 { 'event': 'DUMP_COMPLETED' ,
   'data': { 'result': 'DumpQueryResult', '*error': 'str' } }
diff --git a/qapi/rocker.json b/qapi/rocker.json
index dd72e02..28a4eab 100644
--- a/qapi/rocker.json
+++ b/qapi/rocker.json
@@ -26,6 +26,12 @@
 # Returns: @Rocker information
 #
 # Since: 2.4
+#
+# Example:
+#
+# -> { "execute": "query-rocker", "arguments": { "name": "sw1" } }
+# <- { "return": {"name": "sw1", "ports": 2, "id": 1327446905938}}
+#
 ##
 { 'command': 'query-rocker',
   'data': { 'name': 'str' },
@@ -84,11 +90,21 @@
 ##
 # @query-rocker-ports:
 #
-# Return rocker switch information.
+# Return rocker switch port information.
 #
-# Returns: @Rocker information
+# Returns: a list of @RockerPort information
 #
 # Since: 2.4
+#
+# Example:
+#
+# -> { "execute": "query-rocker-ports", "arguments": { "name": "sw1" } }
+# <- { "return": [ {"duplex": "full", "enabled": true, "name": "sw1.1",
+#                   "autoneg": "off", "link-up": true, "speed": 10000},
+#                  {"duplex": "full", "enabled": true, "name": "sw1.2",
+#                   "autoneg": "off", "link-up": true, "speed": 10000}
+#    ]}
+#
 ##
 { 'command': 'query-rocker-ports',
   'data': { 'name': 'str' },
@@ -219,9 +235,23 @@
 # @tbl-id: #optional flow table ID.  If tbl-id is not specified, returns
 # flow information for all tables.
 #
-# Returns: @Rocker OF-DPA flow information
+# Returns: rocker OF-DPA flow information
 #
 # Since: 2.4
+#
+# Example:
+#
+# -> { "execute": "query-rocker-of-dpa-flows",
+#      "arguments": { "name": "sw1" } }
+# <- { "return": [ {"key": {"in-pport": 0, "priority": 1, "tbl-id": 0},
+#                   "hits": 138,
+#                   "cookie": 0,
+#                   "action": {"goto-tbl": 10},
+#                   "mask": {"in-pport": 4294901760}
+#                  },
+#                  {...more...},
+#    ]}
+#
 ##
 { 'command': 'query-rocker-of-dpa-flows',
   'data': { 'name': 'str', '*tbl-id': 'uint32' },
@@ -281,9 +311,28 @@
 # @type: #optional group type.  If type is not specified, returns
 # group information for all group types.
 #
-# Returns: @Rocker OF-DPA group information
+# Returns: rocker OF-DPA group information
 #
 # Since: 2.4
+#
+# Example:
+#
+# -> { "execute": "query-rocker-of-dpa-groups",
+#      "arguments": { "name": "sw1" } }
+# <- { "return": [ {"type": 0, "out-pport": 2,
+#                   "pport": 2, "vlan-id": 3841,
+#                   "pop-vlan": 1, "id": 251723778},
+#                  {"type": 0, "out-pport": 0,
+#                   "pport": 0, "vlan-id": 3841,
+#                   "pop-vlan": 1, "id": 251723776},
+#                  {"type": 0, "out-pport": 1,
+#                   "pport": 1, "vlan-id": 3840,
+#                   "pop-vlan": 1, "id": 251658241},
+#                  {"type": 0, "out-pport": 0,
+#                   "pport": 0, "vlan-id": 3840,
+#                   "pop-vlan": 1, "id": 251658240}
+#    ]}
+#
 ##
 { 'command': 'query-rocker-of-dpa-groups',
   'data': { 'name': 'str', '*type': 'uint8' },
diff --git a/qapi/trace.json b/qapi/trace.json
index 3ad7df7..2bfda7a 100644
--- a/qapi/trace.json
+++ b/qapi/trace.json
@@ -62,6 +62,13 @@
 # an error is returned.
 #
 # Since: 2.2
+#
+# Example:
+#
+# -> { "execute": "trace-event-get-state",
+#      "arguments": { "name": "qemu_memalign" } }
+# <- { "return": [ { "name": "qemu_memalign", "state": "disabled" } ] }
+#
 ##
 { 'command': 'trace-event-get-state',
   'data': {'name': 'str', '*vcpu': 'int'},
@@ -87,6 +94,13 @@
 # error is returned.
 #
 # Since: 2.2
+#
+# Example:
+#
+# -> { "execute": "trace-event-set-state",
+#      "arguments": { "name": "qemu_memalign", "enable": "true" } }
+# <- { "return": {} }
+#
 ##
 { 'command': 'trace-event-set-state',
   'data': {'name': 'str', 'enable': 'bool', '*ignore-unavailable': 'bool',
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [Qemu-devel] [PATCH v5 16/17] docs: add qemu logo
  2016-11-17 15:54 [Qemu-devel] [PATCH v5 00/17] qapi doc generation (whole version, squashed) Marc-André Lureau
                   ` (14 preceding siblings ...)
  2016-11-17 15:55 ` [Qemu-devel] [PATCH v5 15/17] (SQUASHED) move doc to schema Marc-André Lureau
@ 2016-11-17 15:55 ` Marc-André Lureau
  2016-11-18 12:52   ` Markus Armbruster
  2016-11-17 15:55 ` [Qemu-devel] [PATCH v5 17/17] build-sys: add qapi doc generation targets Marc-André Lureau
  2016-12-05 16:53 ` [Qemu-devel] [PATCH v5 00/17] qapi doc generation (whole version, squashed) Markus Armbruster
  17 siblings, 1 reply; 48+ messages in thread
From: Marc-André Lureau @ 2016-11-17 15:55 UTC (permalink / raw)
  To: qemu-devel; +Cc: eblake, armbru, Marc-André Lureau

The pdf (needed by texi2pdf for vectorized images) was generated thanks
to inkscape, from the pc-bios/qemu_logo.svg file.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 docs/qemu-ga-ref.texi  |   4 ++++
 docs/qemu-qmp-ref.texi |   4 ++++
 docs/qemu_logo.pdf     | Bin 0 -> 9117 bytes
 3 files changed, 8 insertions(+)
 create mode 100644 docs/qemu_logo.pdf

diff --git a/docs/qemu-ga-ref.texi b/docs/qemu-ga-ref.texi
index 02ecdb7..f7ed73e 100644
--- a/docs/qemu-ga-ref.texi
+++ b/docs/qemu-ga-ref.texi
@@ -6,6 +6,10 @@
 
 @settitle QEMU Guest Agent Protocol Reference
 
+@iftex
+@center @image{docs/qemu_logo}
+@end iftex
+
 @copying
 This is the QEMU Guest Agent Protocol reference manual.
 
diff --git a/docs/qemu-qmp-ref.texi b/docs/qemu-qmp-ref.texi
index ccc03cb..0f7e9e4 100644
--- a/docs/qemu-qmp-ref.texi
+++ b/docs/qemu-qmp-ref.texi
@@ -6,6 +6,10 @@
 
 @settitle QEMU QMP Reference Manual
 
+@iftex
+@center @image{docs/qemu_logo}
+@end iftex
+
 @copying
 This is the QEMU QMP reference manual.
 
diff --git a/docs/qemu_logo.pdf b/docs/qemu_logo.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..294cb7dec50de73c786925671300fb0abdf9d641
GIT binary patch
literal 9117
zcmd5?2|QF^`%elfQWRNlwoqesGqx7H>`T^?!C)*ini-<BC~Nl6W=WPxi^$S1Q4vy>
zlq^4#U7MYvi2uDarj$PK|9#)j`*}a_T%U38bDnd~^E~G{-*e9Qj*O|64h*S?<(4TL
z&Yk5(0|<cP<iV|`2EYx8WH-7yfB{A3+yDT;bx2-xA{G4aMW7S4h|UxjBKOXn+%!6s
zNbu(NPw}?OkJ*n~H}rXvLPD>LewJPiPq*yuGb;qMxXf2Yrrc>;>)5rxe(}mw!_IBZ
zBWn5r%A#k#rGWQNo&)#KDAvR8x0=tc_AK7FZkNj5=6K3XBu`00v0e4uQlqTX<bbK(
z5l%ap<hk*j0Hy6sy2Tj^ZCrgkhexBQ)yk$ul*@YTF4>a{EK^pC6URTA>12kjw8+SN
zFL;DT>nnVNJ9Uq*i!-qH#ln^D>ACfRJ_j`?HMf@?@jNTxflU55Zj$}?+0HD(+jedv
zsK^#=)qCfsdYUJ$wa)c?4(M-vH?`g0kBxo1J)9j`)_>X;B@~wx>R4K+FqOBp`!xB!
zYKk&q;3}T*uAA-Y$KeWcRpI5+{BuFY&Y#omOBB^5Vs^djKT6SmH9b!`dlmmaNI~J^
zjhaIL<6~y+qD@B~K30`}Of;%hE1bESUHuGxc_QqQg4HY2nclzcN$iJ3-3#cc0hp{c
zy<3R6;a%}Tr^$xj_YZd`?t32RE2i$=QC7C+f&?{Ht6y(`IGs*^kgG1CxPJQO;rX-K
zqbvRQ-n}mIZI9i->s9SXlwd0Z-)STpY7g`9DQu+Vcix|ylVCSlqiqSJKCGLROGORi
z;~vdL4}>0hReysnT{AIsU5Z=eLjIN9LeG(#e>oLhxsg(nPCXa|sBAxwcvHo$7qz`L
zzF`mF+!cqM>Bn3jyHyX}d#uXcUE<oUZ(m@@uaTPiUbNU(k<2b+%ZA>)Nqdckppx$%
z9tC}RYDDtvV8xkqOBHjhW=1uKc7gM`fjT2i$CVqd1WajXh4ZG~Fo(5ux(Q^;S5-bK
zNn(qtkEuUX-|kNw(tZw%jR}6;;H-4&+FQHLdyx1w=eKl{Gfbac=L;<lc6Sa?vJ_p{
ztr)sFK3^qBu*MU1B-?Jw5dZsZQ}p#qT-iI~jz|sFDfm_DTz<dym<U#uTiw~Kps-zC
z)Fxe=m;T<;hcj_i_RAe7R62MU0Ef9-Wh%03Z|ay64OZ0bpB{V+yX(KL<!YB>e~?hj
z?(4f_tdx|(I)fX-(!c8lWJpMzX6LS4Qx{$`F=meR!Q1T~eXPqX96>(6f^#T+X4P{x
zJ7+{w-KxEw_B^GEhSDO1JcG6ww|tVHemwOBU*2~3q|GI{2V0JEcDO6K2y1TlS=)bT
zm4ZRAs2E$v+a1DTIf1}U!UcH_r~GQ~*Xhxw2k=<uvphOgTa(Hhc#pn+o*w>?qU%+d
zWyVR<-GG(a%7(!Nx~Jp(R@-a#zSo$qHs&{TUt5Q-Bp-Rj71(Y~|NF3nPiyLJY{r<7
zLJ-VRcOx7A(8*O5oK9QWKAdsVnHSE{W;bnEpo~2feFA&S7roKc#Woy2-otTl%VunR
zabGKE;)441crKUVgF|_yMZ-IdbKUsd>LN#Pv1NbX8!sFwc86>9Wn5eS%?<UNrHwt-
znZjy@EbDqjB3^Uxb3M8kmVXbW8i&gay8--lpY3*#gkPnfj)>3cQi7=TRk_t$f(16+
zkiH~e|5<FtakOmZ>X?VQW|D_n){%QJ;IzVeIHHxq8p$WRMQTz#w5PAfrQC5`{lSU<
z>6lPkrP2zCAiSbEX2r(67JB%1LK}BpIbpaBqkA^imU^iw?Q9BcqY3`+b9N?O^|$=f
z0{EWBM}|AJjSfbIE0n0e!>XiOJUabGQZ^w)CuRoUSJR+Hl=4^LuJgNosy^7V`Mac~
zYk9rn#<B>C1t1oASf}}pySTiteAizyd9I}=WkLm%Ik*bG3v83fR#y|<EatOEa_S+$
zp>+MoLdCIf-}Zj{fE?c<nHnA5^)RE#dcU;*M;f7EZJI)?zmK~5>#$4q#{>7Cm>U~E
z-*tR$a{S%Y_y&_ZC)f;gtWI(h$u5hy3*JG94Z$?@9|US78m|l~K#!;&fX;w$ZGXD1
z1s#NMKurx2fFC#k0X_h5%{>4TfIAbwZxGZM%V<h~5)1z!T^b(1v!0n+a0_>W3yJK;
zXoKdC1+Y*#sOgRaaE#kUJsk>}zNA-I7y^a>l$SIH>Ma_LM1W$}GGrOxLj>Ta1UDkX
z15*N(2sVUdRfHQ6T}T8Cia%fviVy$_k5t6qFbE|8udIks#vrgb-~gkhCTIXyl?FhI
z9CJm}rBZx-mKeqCiy>u2zyg3<QVC?557e)7086Y7(5b!*i=eP*f?8TcKaw-iTvr29
zS@hYQNTc{toryF68V7S|wPd*nIlyvJ$CvC(CsD|Z?okY5wJ6>M5}Dx>l4U<)(M1GH
zfc#Bh3?P?w@pJ6KHT$3M;zxgs<`wbEI1E~8vCCh3fQEW8=RlvKBw9=ch9l_3STO=b
z@uE;Id<f3qQ2r7qu#j0!p}NpOcaTUWMJ!Soi^BoRctsQ*g}_3Q`lA?&Rm5YJ(KsyA
zxW%THM{cnI%zZHUU?0%PF~1mzu~^KC5ZD)EteD~@BgWbrF$?}RZ*{2zR_cJ&;hL2H
zA$OUnxy)S_pe)%6A{ld=k)MCs3qqbTXONAFlLyh6&d{{*b)qw-unvXl&8!RFF!Yd&
zM-ArW1?Mdujl!d`01ByyM4^<BH~^`HQ^aGCI6M+S;*=Ee2<4xt_+xHCjesA>&L8l$
zDEnEm2yzT~V?y1|oy=0^RAt_?@^@KvnmZB=;wqFFKhbe<k}rh|$OvW#mP*0K;eW;v
zsN@$YfdoI%WJ%h9oq>Np_S#J*7*jpCRU^Csfw7Wn=w7)(x^I>7@@0WBWQ%hYh53O#
z5U8`zhiR`7f#%5=4$x3TUvCnbLL<=`ISbbXkjz5>Xuu+TEwBGe=;0PnbKvl?W)fpv
zVKfgdGmM)(`WApI!HdQ?L$FX0S}|C5u_h$LzJH90V0wVpBFsauwN&scW+9>V>)+~P
zN%JfO{<UeA@yuNM9UvGUFpI$X|Ifl92BnBc;;|SMfJcMJG!%%4;IvpG#o-l|5jZqP
z`9BPTjJ11l9{e14B(z{JrW5GPzvknTY-XhS4-j19&VSd*AA1?lo}W&lmTo^5Pr;}k
zi^3m|!H_@9dGzl<9})pBdMKPSn48cjqd?fhVnM{iD<YLKcyQ=JY{a54pp2P=|83|~
z1{mZ1zXyGVmJH~VVOwRn9QqhTwwS9wp%1lmSM*OnA0ru-?%bB*2eba4ppVfFbCU_J
zIsZTOp;+4sXil+`?jLh6P|`6qkpKonhF=zlVe7R%`>M9QRqq-(meT1)<*%6y_34w^
zGXdmB0D|izgEjanS4GSAZ#2iqOz_EFmELzFU4SoIh>uS`L)(xP8KlLfvHEqooSA^T
zQPK^e9ogaj;mPyU;i)-ds!bRDRGL1Fyg4yFD>1t;ds+R#J5+;EldU@Yq;-qs>xa|j
z{Mabcs73PrfN8IVBGtQIQP0YQw^=nC-@LE=pl_XX%YaJ}u|0M2X6UtpV%)L%JFg${
z(5`t&nKrBT%6c(zBckWXTa~aeuZr(CJzZaXdZ6hkS$Zb#UBi~7O;Hi#isUu39twla
zb~?5kd}OJiJE_V=U((L-NNm_?yz{cF@J;jRjJ@P6_uD(}N3TiKIkP#fIJKkS$tW*q
zHiqtod=Wn)_2#14TLB7Cm256!^I}Kkbc$uPRclBA8fG2G(-##OuK4#eNgLP3ZSAlJ
zg?`1aTXJcndy=8$Js&<a?7T}kxLKs_Sd7P@Uu;2}?Wa}n$PCh>9Pf(n&4%9X*0G)<
zaPfrHkHIS1!?wP?XV-h~{OXgZSuWad>um*#^(^+`tk3NTNXzYDFWJXl5HuVS=O=<u
z^ffrwSRj-lP!Uh2Tn;=#@HzZtr!aA|kzD?fe$vgSemT#YvcDJKFAuQId!BpWn|$={
zW^bcnnU2iQbnokar<IIFoW(M`ZY9|neD?4T^$nfBUffySIUm*H<=xV^O3*P^NPKPV
zr@i94*<MuP#VhqXr0sLc+V<67Bi!&VDeEk%DXyHqaGSCvb6s12|5vpn3+@^Do$)5U
zm#^!-9;A5`*vYFNEs68WimXU;=GyOtroEgE>NL1Nb~nXQf)X4pe%|Zw?Yjlp@x?;w
zmo1)4k;>G2IA;i7Kn3(Y&P<QY7VTrH)Psh`eh!MtpYsHEO%{)bevLd|_m1bIOp!&=
zjq^36Co#UC{2U1;r`vBj^aKQkf6Xedc+goMtaR?1^x)Q*NSI4m;L1DQlQXejpS0Ju
zA3AYqSJT1nuCuT1+{2YFv>iDv1>Y9%MkYYu+2n%k!brulr-$BFwvH89wjR6zw@`n)
ze$JxyM9~<9Fl;ZhU`1JgpRo4d^rC0qRLlvDzO7#D=r}`@lg9GBp|h|+$xpIh1zvga
zMZOoy>BTxpHR(Mxr8f!)zaUt&9l&shN0&wqyKH+_5(H1e)jT73H<|h8PEz5!q$Z03
ztt34v`SOi0;oezDUCRiL7DLQI)g3M2t9yO(PbLLR^*L9-L^(ZsGXoSnUtRLw8aohT
z$Jyary(`i019oEHSA|b5sihY%_ww#vYM7oZ2n66g9W@`-R9_^l8SuD2ho0Y{U3x}L
zC*rj&zkRjTnxcH(GqcjFwXSO}CBYI>?NrJ>9JLiZ8a5MU^sXoMVmRB~gpXT_FB)B|
zqaQNzvkPnyFv~vR_-4+*JHao=SRmR%+T^O&+fZIJ#V?Un(%C~kJj#02J{k8yu`b&v
z8FD2%_66?z5b|l|MAPlzW;^%j2W@NY1Or;h&6^v`Xs0Lxs`5_No5ycwbgF1q7RVYT
z$Pq4w(4O#miY3N6p(WPe<%HP|mFDH4g`U(_4f<5u>>lZfx@MzmFKH_!;azs#s=^0r
z|2R-uz2;))q^}GtStcHPrZ(@KQ)^)fWup3B0WVC#qD{CUw;$#u<4GuZztTnTXt_uH
zsoWLkdZsa+L1tG5yJt2o1ieaR+tPLFcx-c5-I?Un$?veTvML!#hjU7`!LF;k>Luga
zKOd;bW!s`%B{RSI_}B0XgQgfcxb$G~M&Zy6`4PJ^8WTgxlxkOe*Ga<7Zom26K+V8>
z?K%Ieo7W*sByLZQze&PnzZnsqQ#L5R*zvhOv!R;u4et8I%lB~GwRp14%;x~@*LIm3
zKkx$skrxlTK2V+S5#^uqvwU{{R{fE*BHPWOqh<wBS6U4D3qu~^47Ug6??|oBY&wuh
zQy2OoY2qpWI+8F=`aBhsF64ibEH*Z;7P1NPY!-dmR3TF&Z2CdvC%2CwPrHv>q}AC)
z$ma9C)Uvqr*eRmA=>xC08`a6Ey*2BYp|JBS_mNByoe6h-P}gh_|0;_-;^ZrRI9X1O
z<1m%;Xt=*|!aJAYser!j4Yv~g32VYz_~EBM?TChMIU)oHPj(m?B<tT!j7RX;+Z3z&
zC5djXf(@2r>4)U3rfFY#)+0+Sf2%U(ur|AqhSdBbx4-V>m9c@4_=#GT{X!A&bt%>}
z`+HI<4%!{E>UqKKCNQ5E{6fI^@TZAFje9X8=`&M_io5Eo<;5-w&G>6a&Dr!wv7f;B
zoPDexvVPM})0xv6=PPEpA6d_&2Jt_gea^pCbTrcWQ$l|}-`k@&pbI@lUZK05v)_z;
zv~)OOw6c5HI5w~&%y@lF>87o*GvijP-bzQ~cto#$UC%jIEyep)N5NrNf5M$>J*K)@
zYW<qJ(hgGNo)P}dVb;9G&#eV`#pMz>?GNQSh;n@ySzp6W#PbQvvisZi+}k>2KN3GH
z@I|&;CPbZk_`zD22SIOMRMoy&-S*ijr86m7u%mRM3SU@MLQEFCttxi6IXabh2yjys
zuoG|yul}Mpr_<BQ{m8tnh4WFAHf>$nwSY8SUQ)?zjx?$7?|Sn?N?^V-Ar%EAe;q<W
zMH6?j!0}w?%nA#U{S)#bJc?diXST_knF!mM$bL%5iz)HtXnED!D%-s#1bfqdKscn?
zh)DI_Zq*ujmB{wDW$4v(|NVqb!!i4~V-JMrpu7yZzx0}+j=hf66}8d7m89)|p(JdR
zm8rN@rQ?0h+LbdGE6hW}1jH?EGV*MCf@aw#*g|I6XL2eeLuT(DqDURjXiPVihzwCV
ze#`WlPi)0X_FX8%LBhzJ<o^934{(HrA%EqkkB9v0P>2HF!}6~iYU0qSF->+X#|XwJ
ztb!gQA!li=;^M1xRo;^5?4ftwVv~l7#vU_e?ej^zhA+*`#9`Zo%^J$?stMow^q0{U
z!<Y3vN%s^wQOXW)GY}UW`~4FY5{3S<C2Z_*9F-197Lb)CoLY~IiA}-@3z?i6&zBPy
z+#bmjCGsh$&k5NQIl7Ht7;h32q4U!H&W;Jfl-zmI;VL;nG6FGi<g)0N=ugLl2<c-g
z=XcQiR0KoUudV6f2z{CIWF;4`Z*6)-qvrwYb&=K*giD?UZ$!D{w%j#Wc5m$p8<pQL
z7E;=YzbLqS1h`xmtx0|$l%+ZVNN_Yqrg(+bjc?s1cUP?Dd8c%N0E#qUXC(Cg@jf%5
zI5U7#s3x4ZCTe9?m=BT5T5i91bnQ3)0iwR%q*Pq+d`{xYsd;_9&lwW5=f^qBJN4FZ
zKU)7x<?PwG3w+!8+4X#Mp9R}U7dBqZ*Qiw8AZRb$_mR)Q*g3M%`bw-IvL9Bx8#A84
zJ{H0^pq+K<e4ceV|EZ3F_z}6kIznpMt;>AQR+1jaM|0WPX|0p|7%~zpF5tI1`P9C0
zNnes#oQjJn7dzL>kPk_hGMZ}T?+ZrFXBLe$W@M{<KarWY;J0F-{A|rUHy`5G_do2Z
zS*#Ga_e3e75iC9c#i9b2?p}YM{{O@lEoCzfnAVb?0DPsBn~oatZjkmS*O(_c1nSv0
z4|6)&h-L}%RdX&J6AU-c;rxPC%_MxyJ~3QY`*@dmBfD!}LvpZzO@fZlA!?eKiDOE`
zbgqv2%^G}snD;aM^ji*DnK6@<=Tns0$}S*!)7Vy=FyTm;<R5wn*A(ILjM|0Id;!?B
z2JJDRT5sxl9rbwo*G7?w18;e?W;<ruB`b2iJayIQJZ)epoG`(UlGoyV)gE&Nw~%+O
zTIlw}oXY;mi%KEsT1BLyE8jW?6VFs1dvHC8vdiluR)%UrvADg-qM@%Z5LsDqV^omR
zP8<9~Kv_%*6j1aJf(AU~|70ncrwoWc*CNuKsU#mdg~~WkKq6y;H^`|Pm>S#aYHimd
zdJwF9EkL>&ra|#?`Jtpk@FsZ$0CLQ708)@=7J3ofKti0!u0YK#l5sHbNU99LFd%(|
zR0auFB$CCl!u9C{FOu^fvYQtXKrkBFLvw~Gd1def0v$;pBcW#)3XNq5GzmU>M3S33
z$l8O=Fhmw~qPG=*XV!-v^jHs&5n#FyQD8X07%V+`vA72)$9~3y*&d5zv;?_bsEi@i
zAkc_Vd;d5-Br1)r=}w@s!eB^Pe8w|vg<AL-cz`i9%#)oq8GHv0z59S$lgN9>G!jd|
z=-ry+LU)HaZHP|z&s<7)C?-oUWo2mQExqta)FKrGx1{VNlc3l10AqeGjU_~9uvjo=
zETK0nG=|{@BqkLPwQ9~{)hNEuTbCuT7f?$N!kXYu0KlvS<eUj4Dg^)uT#TX;Am>h}
z`>4P{@(&ccQ3*coBxjl;h3Y2H%sx{p#l_bd{7Jz-Uu_9XvpgL%31Dtg+!#(U+*y20
z^s|ybmFUWi06^eE{P+M$SS-joy8??ckZ%J~4*CFOmJEqSER!J+5V`uB9tNbUf0Ln*
z2+-7D_0UKtjee71miK|dAV9w2S3MjayG({u!Y{W)8O(&=>Y@<P+l^n_17QaQq2FZK
z<?W#n7%(G#)k7ncmi2)~L3HtNdT59||4pWZUS<m#ue7WW40?H-F=+I%_+c>kWpT!#
zme<9h(989(%VUbef|&Q4&3F`cnSFRP4x9=s-{@2Vh+b62FK8@CM~G0aL8xd>q0j*o
jl(S6i&?mc6z?YSbXU0-Upwb!Hi3k1VmXXmm(c%6--IUSm

literal 0
HcmV?d00001

-- 
2.10.0

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [Qemu-devel] [PATCH v5 17/17] build-sys: add qapi doc generation targets
  2016-11-17 15:54 [Qemu-devel] [PATCH v5 00/17] qapi doc generation (whole version, squashed) Marc-André Lureau
                   ` (15 preceding siblings ...)
  2016-11-17 15:55 ` [Qemu-devel] [PATCH v5 16/17] docs: add qemu logo Marc-André Lureau
@ 2016-11-17 15:55 ` Marc-André Lureau
  2016-11-18 12:31   ` Markus Armbruster
  2016-12-05 16:53 ` [Qemu-devel] [PATCH v5 00/17] qapi doc generation (whole version, squashed) Markus Armbruster
  17 siblings, 1 reply; 48+ messages in thread
From: Marc-André Lureau @ 2016-11-17 15:55 UTC (permalink / raw)
  To: qemu-devel; +Cc: eblake, armbru, Marc-André Lureau

Generate and install the man and html version of QAPI documentation.

Add it also to optional pdf/dvi/info targets.

Also support plain-text targets docs/qemu-ga-ref.txt & docs/qemu-qmp-ref.txt.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 Makefile           | 56 +++++++++++++++++++++++++++++++++++++++++++++++-------
 .gitignore         | 11 ++++++++++-
 docs/qmp-intro.txt |  3 +--
 3 files changed, 60 insertions(+), 10 deletions(-)

diff --git a/Makefile b/Makefile
index 3617736..cc1c46e 100644
--- a/Makefile
+++ b/Makefile
@@ -91,6 +91,8 @@ HELPERS-$(CONFIG_LINUX) = qemu-bridge-helper$(EXESUF)
 
 ifdef BUILD_DOCS
 DOCS=qemu-doc.html qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8
+DOCS+=docs/qemu-qmp-ref.html docs/qemu-qmp-ref.7
+DOCS+=docs/qemu-ga-ref.html docs/qemu-ga-ref.7
 ifdef CONFIG_VIRTFS
 DOCS+=fsdev/virtfs-proxy-helper.1
 endif
@@ -266,6 +268,7 @@ qemu-ga$(EXESUF): QEMU_CFLAGS += -I qga/qapi-generated
 gen-out-type = $(subst .,-,$(suffix $@))
 
 qapi-py = $(SRC_PATH)/scripts/qapi.py $(SRC_PATH)/scripts/ordereddict.py
+qapi-py += $(SRC_PATH)/scripts/qapi2texi.py
 
 qga/qapi-generated/qga-qapi-types.c qga/qapi-generated/qga-qapi-types.h :\
 $(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
@@ -395,6 +398,11 @@ distclean: clean
 	rm -f qemu-doc.vr
 	rm -f config.log
 	rm -f linux-headers/asm
+	rm -f qemu-ga-qapi.texi qemu-qapi.texi
+	rm -f docs/qemu-qmp-ref.7 docs/qemu-ga-ref.7
+	rm -f docs/qemu-qmp-ref.txt docs/qemu-ga-ref.txt
+	rm -f docs/qemu-qmp-ref.pdf docs/qemu-ga-ref.pdf
+	rm -f docs/qemu-qmp-ref.html docs/qemu-ga-ref.html
 	for d in $(TARGET_DIRS); do \
 	rm -rf $$d || exit 1 ; \
         done
@@ -431,9 +439,12 @@ endif
 install-doc: $(DOCS)
 	$(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)"
 	$(INSTALL_DATA) qemu-doc.html "$(DESTDIR)$(qemu_docdir)"
+	$(INSTALL_DATA) docs/qemu-qmp-ref.html "$(DESTDIR)$(qemu_docdir)"
 ifdef CONFIG_POSIX
 	$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1"
 	$(INSTALL_DATA) qemu.1 "$(DESTDIR)$(mandir)/man1"
+	$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man7"
+	$(INSTALL_DATA) docs/qemu-qmp-ref.7 "$(DESTDIR)$(mandir)/man7"
 ifneq ($(TOOLS),)
 	$(INSTALL_DATA) qemu-img.1 "$(DESTDIR)$(mandir)/man1"
 	$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man8"
@@ -441,6 +452,8 @@ ifneq ($(TOOLS),)
 endif
 ifneq (,$(findstring qemu-ga,$(TOOLS)))
 	$(INSTALL_DATA) qemu-ga.8 "$(DESTDIR)$(mandir)/man8"
+	$(INSTALL_DATA) docs/qemu-ga-ref.html "$(DESTDIR)$(qemu_docdir)"
+	$(INSTALL_DATA) docs/qemu-ga-ref.7 "$(DESTDIR)$(mandir)/man7"
 endif
 endif
 ifdef CONFIG_VIRTFS
@@ -528,9 +541,9 @@ ui/console-gl.o: $(SRC_PATH)/ui/console-gl.c \
 	ui/shader/texture-blit-vert.h ui/shader/texture-blit-frag.h
 
 # documentation
-MAKEINFO=makeinfo
+MAKEINFO=makeinfo -D 'VERSION $(VERSION)'
 MAKEINFOFLAGS=--no-headers --no-split --number-sections
-TEXIFLAG=$(if $(V),,--quiet)
+TEXIFLAG=$(if $(V),,--quiet) --command='@set VERSION $(VERSION)'
 %.dvi: %.texi
 	$(call quiet-command,texi2dvi $(TEXIFLAG) -I . $<,"GEN","$@")
 
@@ -542,7 +555,11 @@ TEXIFLAG=$(if $(V),,--quiet)
 	$(call quiet-command,$(MAKEINFO) $< -o $@,"GEN","$@")
 
 %.pdf: %.texi
-	$(call quiet-command,texi2pdf $(TEXIFLAG) -I . $<,"GEN","$@")
+	$(call quiet-command,texi2pdf $(TEXIFLAG) -I . $< -o $@,"GEN","$@")
+
+%.txt: %.texi
+	$(call quiet-command,LC_ALL=C $(MAKEINFO) $(MAKEINFOFLAGS) --plaintext $< -o $@,\
+	"  GEN   $@")
 
 qemu-options.texi: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool
 	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@")
@@ -556,6 +573,12 @@ qemu-monitor-info.texi: $(SRC_PATH)/hmp-commands-info.hx $(SRC_PATH)/scripts/hxt
 qemu-img-cmds.texi: $(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/scripts/hxtool
 	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@")
 
+qemu-qapi.texi: $(qapi-modules) $(qapi-py)
+	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi2texi.py $< > $@,"GEN" "$@")
+
+qemu-ga-qapi.texi: $(SRC_PATH)/qga/qapi-schema.json $(qapi-py)
+	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi2texi.py $< > $@,"GEN","$@")
+
 qemu.1: qemu-doc.texi qemu-options.texi qemu-monitor.texi qemu-monitor-info.texi
 	$(call quiet-command, \
 	  perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu.pod && \
@@ -587,16 +610,35 @@ qemu-ga.8: qemu-ga.texi
 	  $(POD2MAN) --section=8 --center=" " --release=" " qemu-ga.pod > $@, \
 	  "GEN","$@")
 
-dvi: qemu-doc.dvi
-html: qemu-doc.html
-info: qemu-doc.info
-pdf: qemu-doc.pdf
+docs/qemu-qmp-ref.7:
+	$(call quiet-command, \
+	 perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu-qmp-ref.pod && \
+	 $(POD2MAN) --section=7 --center=" " --release=" " qemu-qmp-ref.pod > $@, \
+	 "GEN","$@")
+
+docs/qemu-ga-ref.7:
+	$(call quiet-command, \
+	 perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu-ga-ref.pod && \
+	 $(POD2MAN) --section=7 --center=" " --release=" " qemu-ga-ref.pod > $@, \
+	 "GEN","$@")
+
+dvi: qemu-doc.dvi docs/qemu-qmp-ref.dvi docs/qemu-ga-ref.dvi
+html: qemu-doc.html docs/qemu-qmp-ref.html docs/qemu-ga-ref.html
+info: qemu-doc.info docs/qemu-qmp-ref.info docs/qemu-ga-ref.info
+pdf: qemu-doc.pdf docs/qemu-qmp-ref.pdf docs/qemu-ga-ref.pdf
 
 qemu-doc.dvi qemu-doc.html qemu-doc.info qemu-doc.pdf: \
 	qemu-img.texi qemu-nbd.texi qemu-options.texi qemu-option-trace.texi \
 	qemu-monitor.texi qemu-img-cmds.texi qemu-ga.texi \
 	qemu-monitor-info.texi
 
+docs/qemu-ga-ref.dvi docs/qemu-ga-ref.html docs/qemu-ga-ref.info docs/qemu-ga-ref.pdf docs/qemu-ga-ref.txt docs/qemu-ga-ref.7: \
+docs/qemu-ga-ref.texi qemu-ga-qapi.texi
+
+docs/qemu-qmp-ref.dvi docs/qemu-qmp-ref.html docs/qemu-qmp-ref.info docs/qemu-qmp-ref.pdf docs/qemu-qmp-ref.txt docs/qemu-qmp-ref.7: \
+docs/qemu-qmp-ref.texi qemu-qapi.texi
+
+
 ifdef CONFIG_WIN32
 
 INSTALLER = qemu-setup-$(VERSION)$(EXESUF)
diff --git a/.gitignore b/.gitignore
index 3d7848c..d0905c3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -39,7 +39,7 @@
 /qmp-introspect.[ch]
 /qmp-marshal.c
 /qemu-doc.html
-/qemu-doc.info
+/qemu-doc.info*
 /qemu-img
 /qemu-nbd
 /qemu-options.def
@@ -109,6 +109,15 @@
 /pc-bios/optionrom/kvmvapic.img
 /pc-bios/s390-ccw/s390-ccw.elf
 /pc-bios/s390-ccw/s390-ccw.img
+/docs/qemu-ga-ref.html
+/docs/qemu-ga-ref.txt
+/docs/qemu-qmp-ref.html
+/docs/qemu-qmp-ref.txt
+docs/qemu-ga-ref.info*
+docs/qemu-qmp-ref.info*
+/qemu-ga-qapi.texi
+/qemu-qapi.texi
+*.tps
 .stgit-*
 cscope.*
 tags
diff --git a/docs/qmp-intro.txt b/docs/qmp-intro.txt
index f6a3a03..60deafb 100644
--- a/docs/qmp-intro.txt
+++ b/docs/qmp-intro.txt
@@ -16,8 +16,7 @@ QMP is JSON[1] based and features the following:
 For detailed information on QMP's usage, please, refer to the following files:
 
 o qmp-spec.txt      QEMU Machine Protocol current specification
-o qmp-commands.txt  QMP supported commands (auto-generated at build-time)
-o qmp-events.txt    List of available asynchronous events
+o qemu-qmp-ref.html QEMU QMP commands and events (auto-generated at build-time)
 
 [1] http://www.json.org
 
-- 
2.10.0

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v5 01/17] qapi: improve device_add schema
  2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 01/17] qapi: improve device_add schema Marc-André Lureau
@ 2016-11-17 17:38   ` Markus Armbruster
  2016-11-17 19:49   ` Eric Blake
  1 sibling, 0 replies; 48+ messages in thread
From: Markus Armbruster @ 2016-11-17 17:38 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

Marc-André Lureau <marcandre.lureau@redhat.com> writes:

> 'device_add' is still incomplete for now, but we can fix a few
> arguments:
> - 'bus' is a common argument, regardless of the device
> - 'id' is an optionnal argument
>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

Reviewed-by: Markus Armbruster <armbru@redhat.com>

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v5 02/17] qga/schema: fix double-return in doc
  2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 02/17] qga/schema: fix double-return in doc Marc-André Lureau
@ 2016-11-17 17:38   ` Markus Armbruster
  0 siblings, 0 replies; 48+ messages in thread
From: Markus Armbruster @ 2016-11-17 17:38 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

Marc-André Lureau <marcandre.lureau@redhat.com> writes:

> guest-get-memory-block-info documentation should have only one
> "Returns:".
>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> Reviewed-by: Eric Blake <eblake@redhat.com>

Reviewed-by: Markus Armbruster <armbru@redhat.com>

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v5 03/17] qga/schema: improve guest-set-vcpus Returns: section
  2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 03/17] qga/schema: improve guest-set-vcpus Returns: section Marc-André Lureau
@ 2016-11-17 17:39   ` Markus Armbruster
  2016-11-18  8:49     ` Marc-André Lureau
  0 siblings, 1 reply; 48+ messages in thread
From: Markus Armbruster @ 2016-11-17 17:39 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

Marc-André Lureau <marcandre.lureau@redhat.com> writes:

> The documentation parser we are going to add finishes a section after an
> empty line.

Is this still true?

> Fix the Returns: section of guest-set-vcpus, and itemize the possible
> return values.
>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

Patch looks good.

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v5 04/17] qapi: fix schema symbol sections
  2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 04/17] qapi: fix schema symbol sections Marc-André Lureau
@ 2016-11-17 17:39   ` Markus Armbruster
  0 siblings, 0 replies; 48+ messages in thread
From: Markus Armbruster @ 2016-11-17 17:39 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

Marc-André Lureau <marcandre.lureau@redhat.com> writes:

> According to docs/qapi-code-gen.txt, there needs to be '##' to start a
> and end a symbol section, that's also what the documentation parser
> expects.
>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> Reviewed-by: Eric Blake <eblake@redhat.com>

Reviewed-by: Markus Armbruster <armbru@redhat.com>

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v5 05/17] qapi: fix missing symbol @prefix
  2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 05/17] qapi: fix missing symbol @prefix Marc-André Lureau
@ 2016-11-17 17:40   ` Markus Armbruster
  0 siblings, 0 replies; 48+ messages in thread
From: Markus Armbruster @ 2016-11-17 17:40 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

Marc-André Lureau <marcandre.lureau@redhat.com> writes:

> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> Reviewed-by: Eric Blake <eblake@redhat.com>

Reviewed-by: Markus Armbruster <armbru@redhat.com>

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v5 06/17] qapi: fix various symbols mismatch in documentation
  2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 06/17] qapi: fix various symbols mismatch in documentation Marc-André Lureau
@ 2016-11-17 17:40   ` Markus Armbruster
  0 siblings, 0 replies; 48+ messages in thread
From: Markus Armbruster @ 2016-11-17 17:40 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

Marc-André Lureau <marcandre.lureau@redhat.com> writes:

> There are various mismatch:
> - invalid symbols
> - section and member symbols mismatch
> - enum or union values vs 'type'
>
> The documentation parser catches all these cases.
>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

Union documentation needs work, but that's out of scope for this patch.

Reviewed-by: Markus Armbruster <armbru@redhat.com>

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v5 07/17] qapi: use one symbol per line
  2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 07/17] qapi: use one symbol per line Marc-André Lureau
@ 2016-11-17 17:40   ` Markus Armbruster
  0 siblings, 0 replies; 48+ messages in thread
From: Markus Armbruster @ 2016-11-17 17:40 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

Marc-André Lureau <marcandre.lureau@redhat.com> writes:

> The documentation parser we are going to add only handles a single
> symbol per line.
>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

Reviewed-by: Markus Armbruster <armbru@redhat.com>

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v5 08/17] qapi: add missing colon-ending for section name
  2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 08/17] qapi: add missing colon-ending for section name Marc-André Lureau
@ 2016-11-17 17:41   ` Markus Armbruster
  0 siblings, 0 replies; 48+ messages in thread
From: Markus Armbruster @ 2016-11-17 17:41 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

Marc-André Lureau <marcandre.lureau@redhat.com> writes:

> The documentation parser we are going to add expects a section name to
> end with ':', otherwise the comment is treated as free-form text body.
>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

Reviewed-by: Markus Armbruster <armbru@redhat.com>

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v5 09/17] qapi: add some sections in docs
  2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 09/17] qapi: add some sections in docs Marc-André Lureau
@ 2016-11-17 17:43   ` Markus Armbruster
  2016-11-30 15:38   ` Markus Armbruster
  1 sibling, 0 replies; 48+ messages in thread
From: Markus Armbruster @ 2016-11-17 17:43 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

Marc-André Lureau <marcandre.lureau@redhat.com> writes:

> Add some more section titles to organize the produced documents.

Either move this after the doc generator gets added, or say something
like "to organize the documentation we're going to generate".

> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

With that:
Reviewed-by: Markus Armbruster <armbru@redhat.com>

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v5 10/17] qapi: improve TransactionAction doc
  2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 10/17] qapi: improve TransactionAction doc Marc-André Lureau
@ 2016-11-17 18:03   ` Markus Armbruster
  0 siblings, 0 replies; 48+ messages in thread
From: Markus Armbruster @ 2016-11-17 18:03 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

Marc-André Lureau <marcandre.lureau@redhat.com> writes:

> TransactionAction is a flat union, document 'type' versions
> exhaustively, and sort list.

"and sort the members".

>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

With that
Reviewed-by: Markus Armbruster <armbru@redhat.com>

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v5 01/17] qapi: improve device_add schema
  2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 01/17] qapi: improve device_add schema Marc-André Lureau
  2016-11-17 17:38   ` Markus Armbruster
@ 2016-11-17 19:49   ` Eric Blake
  1 sibling, 0 replies; 48+ messages in thread
From: Eric Blake @ 2016-11-17 19:49 UTC (permalink / raw)
  To: Marc-André Lureau, qemu-devel; +Cc: armbru

[-- Attachment #1: Type: text/plain, Size: 573 bytes --]

On 11/17/2016 09:54 AM, Marc-André Lureau wrote:
> 'device_add' is still incomplete for now, but we can fix a few
> arguments:
> - 'bus' is a common argument, regardless of the device
> - 'id' is an optionnal argument

s/optionnal/optional/

> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  qapi-schema.json | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 

Reviewed-by: Eric Blake <eblake@redhat.com>

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v5 03/17] qga/schema: improve guest-set-vcpus Returns: section
  2016-11-17 17:39   ` Markus Armbruster
@ 2016-11-18  8:49     ` Marc-André Lureau
  2016-11-18 14:07       ` Markus Armbruster
  0 siblings, 1 reply; 48+ messages in thread
From: Marc-André Lureau @ 2016-11-18  8:49 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: Marc-André Lureau, qemu-devel

Hi

----- Original Message -----
> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
> 
> > The documentation parser we are going to add finishes a section after an
> > empty line.
> 
> Is this still true?

No longer true, the parser only breaks on argument empty line, as explained in cover letter.

I'll bring back the empty lines.

> 
> > Fix the Returns: section of guest-set-vcpus, and itemize the possible
> > return values.
> >
> > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> 
> Patch looks good.
> 

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v5 11/17] docs: add master qapi texi files
  2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 11/17] docs: add master qapi texi files Marc-André Lureau
@ 2016-11-18  9:09   ` Markus Armbruster
  0 siblings, 0 replies; 48+ messages in thread
From: Markus Armbruster @ 2016-11-18  9:09 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

Marc-André Lureau <marcandre.lureau@redhat.com> writes:

> The qapi2texi script generates a file to be included in a texi file. Add
> "QEMU QMP Reference Manual" and "QEMU Guest Agent Protocol Reference"
> master texi files.
>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

Reviewed-by: Markus Armbruster <armbru@redhat.com>

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v5 12/17] qapi: rename QAPIExprError/QAPILineError
  2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 12/17] qapi: rename QAPIExprError/QAPILineError Marc-André Lureau
@ 2016-11-18 10:17   ` Markus Armbruster
  2016-11-18 10:31     ` Marc-André Lureau
  0 siblings, 1 reply; 48+ messages in thread
From: Markus Armbruster @ 2016-11-18 10:17 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

Make the summary line "qapi: Rename QAPIExprError to QAPILineError".

Marc-André Lureau <marcandre.lureau@redhat.com> writes:

> There is nothing specific about expressions in this exception,
> the following patch will use it without expressions.
>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  scripts/qapi.py | 146 ++++++++++++++++++++++++++++----------------------------
>  1 file changed, 73 insertions(+), 73 deletions(-)
>
> diff --git a/scripts/qapi.py b/scripts/qapi.py
> index 21bc32f..4d1b0e4 100644
> --- a/scripts/qapi.py
> +++ b/scripts/qapi.py
> @@ -110,11 +110,11 @@ class QAPISchemaError(Exception):
>              "%s:%d:%d: %s" % (self.fname, self.line, self.col, self.msg)
>  
>  
> -class QAPIExprError(Exception):
> -    def __init__(self, expr_info, msg):
> +class QAPILineError(Exception):
> +    def __init__(self, info, msg):
>          Exception.__init__(self)
> -        assert expr_info
> -        self.info = expr_info
> +        assert info
> +        self.info = info
>          self.msg = msg
>  
>      def __str__(self):

Since we're talking about misnamed / awkward error stuff:

* QAPISchemaError is really a parse error.  __init__()'s schema argument
  isn't a QAPISchema, it's a QAPISchemaParser.

* Method __str__() is mostly duplicated.

How do you like the following untested sketch?

    class QAPISchemaError(Exception):
        def __init__(self, fname, line, col, incl_info, msg):
            Exception.__init__(self)
            self.fname = fname
            self.line = line
            self.col = col
            self.info = incl_info
            self.msg = msg

        def __str__(self):
            loc = "%s:%d" % (self.fname, self.line)
            if self.col is not None:
                loc += ":%s" % self.col
            return error_path(self.info) + \
                "%s: %s" % (loc, self.msg)


    class QAPISchemaParseError(QAPISchemaError):
        def __init__(self, parser, msg):
            col = 1
            for ch in parser.src[parser.line_pos:parser.pos]:
                if ch == '\t':
                    col = (col + 7) % 8 + 1
                else:
                    col += 1
            QAPISchemaError.__init__(self, parser.fname, parser.line, parser.col,
                                     parser.incl_info, msg)


    class QAPISchemaSemanticError(Exception):
        def __init__(self, info, msg):
            QAPISchemaError(self, info['file'], info['line'], None,
                            info['parent'], msg)

The class names are a bit long.  If they result in awkward line breaks,
let's shorten them some.

The "except (QAPISchemaError, QAPIExprError)" become just "except
QAPISchemaError".

Could QAPISchemaParser.__init__() raise QAPISchemaParseError instead?

[...]

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v5 12/17] qapi: rename QAPIExprError/QAPILineError
  2016-11-18 10:17   ` Markus Armbruster
@ 2016-11-18 10:31     ` Marc-André Lureau
  2016-11-18 14:13       ` Markus Armbruster
  0 siblings, 1 reply; 48+ messages in thread
From: Marc-André Lureau @ 2016-11-18 10:31 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: Marc-André Lureau, qemu-devel



----- Original Message -----
> Make the summary line "qapi: Rename QAPIExprError to QAPILineError".
> 

ok

> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
> 
> > There is nothing specific about expressions in this exception,
> > the following patch will use it without expressions.
> >
> > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> > ---
> >  scripts/qapi.py | 146
> >  ++++++++++++++++++++++++++++----------------------------
> >  1 file changed, 73 insertions(+), 73 deletions(-)
> >
> > diff --git a/scripts/qapi.py b/scripts/qapi.py
> > index 21bc32f..4d1b0e4 100644
> > --- a/scripts/qapi.py
> > +++ b/scripts/qapi.py
> > @@ -110,11 +110,11 @@ class QAPISchemaError(Exception):
> >              "%s:%d:%d: %s" % (self.fname, self.line, self.col, self.msg)
> >  
> >  
> > -class QAPIExprError(Exception):
> > -    def __init__(self, expr_info, msg):
> > +class QAPILineError(Exception):
> > +    def __init__(self, info, msg):
> >          Exception.__init__(self)
> > -        assert expr_info
> > -        self.info = expr_info
> > +        assert info
> > +        self.info = info
> >          self.msg = msg
> >  
> >      def __str__(self):
> 
> Since we're talking about misnamed / awkward error stuff:
> 
> * QAPISchemaError is really a parse error.  __init__()'s schema argument
>   isn't a QAPISchema, it's a QAPISchemaParser.
> 
> * Method __str__() is mostly duplicated.
> 
> How do you like the following untested sketch?

I like it, though I would consider it separately from this series. Here I systematically renamed the existing class ("line" since it's about location in file), your proposed change has more implications I don't really want to get into here.

> 
>     class QAPISchemaError(Exception):
>         def __init__(self, fname, line, col, incl_info, msg):
>             Exception.__init__(self)
>             self.fname = fname
>             self.line = line
>             self.col = col
>             self.info = incl_info
>             self.msg = msg
> 
>         def __str__(self):
>             loc = "%s:%d" % (self.fname, self.line)
>             if self.col is not None:
>                 loc += ":%s" % self.col
>             return error_path(self.info) + \
>                 "%s: %s" % (loc, self.msg)
> 
> 
>     class QAPISchemaParseError(QAPISchemaError):
>         def __init__(self, parser, msg):
>             col = 1
>             for ch in parser.src[parser.line_pos:parser.pos]:
>                 if ch == '\t':
>                     col = (col + 7) % 8 + 1
>                 else:
>                     col += 1
>             QAPISchemaError.__init__(self, parser.fname, parser.line,
>             parser.col,
>                                      parser.incl_info, msg)
> 
> 
>     class QAPISchemaSemanticError(Exception):
>         def __init__(self, info, msg):
>             QAPISchemaError(self, info['file'], info['line'], None,
>                             info['parent'], msg)
> 
> The class names are a bit long.  If they result in awkward line breaks,
> let's shorten them some.
> 
> The "except (QAPISchemaError, QAPIExprError)" become just "except
> QAPISchemaError".
> 
> Could QAPISchemaParser.__init__() raise QAPISchemaParseError instead?
> 
> [...]
> 

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v5 17/17] build-sys: add qapi doc generation targets
  2016-11-17 15:55 ` [Qemu-devel] [PATCH v5 17/17] build-sys: add qapi doc generation targets Marc-André Lureau
@ 2016-11-18 12:31   ` Markus Armbruster
  2016-11-21 12:30     ` Marc-André Lureau
  0 siblings, 1 reply; 48+ messages in thread
From: Markus Armbruster @ 2016-11-18 12:31 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

Marc-André Lureau <marcandre.lureau@redhat.com> writes:

> Generate and install the man and html version of QAPI documentation.
>
> Add it also to optional pdf/dvi/info targets.
>
> Also support plain-text targets docs/qemu-ga-ref.txt & docs/qemu-qmp-ref.txt.
>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  Makefile           | 56 +++++++++++++++++++++++++++++++++++++++++++++++-------
>  .gitignore         | 11 ++++++++++-
>  docs/qmp-intro.txt |  3 +--
>  3 files changed, 60 insertions(+), 10 deletions(-)
>
> diff --git a/Makefile b/Makefile
> index 3617736..cc1c46e 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -91,6 +91,8 @@ HELPERS-$(CONFIG_LINUX) = qemu-bridge-helper$(EXESUF)
>  
>  ifdef BUILD_DOCS
>  DOCS=qemu-doc.html qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8
> +DOCS+=docs/qemu-qmp-ref.html docs/qemu-qmp-ref.7
> +DOCS+=docs/qemu-ga-ref.html docs/qemu-ga-ref.7
>  ifdef CONFIG_VIRTFS
>  DOCS+=fsdev/virtfs-proxy-helper.1
>  endif
> @@ -266,6 +268,7 @@ qemu-ga$(EXESUF): QEMU_CFLAGS += -I qga/qapi-generated
>  gen-out-type = $(subst .,-,$(suffix $@))
>  
>  qapi-py = $(SRC_PATH)/scripts/qapi.py $(SRC_PATH)/scripts/ordereddict.py
> +qapi-py += $(SRC_PATH)/scripts/qapi2texi.py
>  
>  qga/qapi-generated/qga-qapi-types.c qga/qapi-generated/qga-qapi-types.h :\
>  $(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
> @@ -395,6 +398,11 @@ distclean: clean
>  	rm -f qemu-doc.vr
>  	rm -f config.log
>  	rm -f linux-headers/asm
> +	rm -f qemu-ga-qapi.texi qemu-qapi.texi
> +	rm -f docs/qemu-qmp-ref.7 docs/qemu-ga-ref.7
> +	rm -f docs/qemu-qmp-ref.txt docs/qemu-ga-ref.txt
> +	rm -f docs/qemu-qmp-ref.pdf docs/qemu-ga-ref.pdf
> +	rm -f docs/qemu-qmp-ref.html docs/qemu-ga-ref.html

Missing: .dvi

>  	for d in $(TARGET_DIRS); do \
>  	rm -rf $$d || exit 1 ; \
>          done
> @@ -431,9 +439,12 @@ endif
>  install-doc: $(DOCS)
>  	$(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)"
>  	$(INSTALL_DATA) qemu-doc.html "$(DESTDIR)$(qemu_docdir)"
> +	$(INSTALL_DATA) docs/qemu-qmp-ref.html "$(DESTDIR)$(qemu_docdir)"

Personally, I'd install .txt along with .html, because the .txt is
perfectly legible, and plain text is easier to read in some situations.
We don't do that for qemu-doc, though.

>  ifdef CONFIG_POSIX
>  	$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1"
>  	$(INSTALL_DATA) qemu.1 "$(DESTDIR)$(mandir)/man1"
> +	$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man7"
> +	$(INSTALL_DATA) docs/qemu-qmp-ref.7 "$(DESTDIR)$(mandir)/man7"
>  ifneq ($(TOOLS),)
>  	$(INSTALL_DATA) qemu-img.1 "$(DESTDIR)$(mandir)/man1"
>  	$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man8"
> @@ -441,6 +452,8 @@ ifneq ($(TOOLS),)
>  endif
>  ifneq (,$(findstring qemu-ga,$(TOOLS)))
>  	$(INSTALL_DATA) qemu-ga.8 "$(DESTDIR)$(mandir)/man8"
> +	$(INSTALL_DATA) docs/qemu-ga-ref.html "$(DESTDIR)$(qemu_docdir)"
> +	$(INSTALL_DATA) docs/qemu-ga-ref.7 "$(DESTDIR)$(mandir)/man7"
>  endif
>  endif
>  ifdef CONFIG_VIRTFS
> @@ -528,9 +541,9 @@ ui/console-gl.o: $(SRC_PATH)/ui/console-gl.c \
>  	ui/shader/texture-blit-vert.h ui/shader/texture-blit-frag.h
>  
>  # documentation
> -MAKEINFO=makeinfo
> +MAKEINFO=makeinfo -D 'VERSION $(VERSION)'
>  MAKEINFOFLAGS=--no-headers --no-split --number-sections
> -TEXIFLAG=$(if $(V),,--quiet)
> +TEXIFLAG=$(if $(V),,--quiet) --command='@set VERSION $(VERSION)'
>  %.dvi: %.texi
>  	$(call quiet-command,texi2dvi $(TEXIFLAG) -I . $<,"GEN","$@")
>  

Defines variable VERSION for all .texi, not just qemu-*-ref.texi, for
simplicity.  Works for me.

> @@ -542,7 +555,11 @@ TEXIFLAG=$(if $(V),,--quiet)
>  	$(call quiet-command,$(MAKEINFO) $< -o $@,"GEN","$@")
>  
>  %.pdf: %.texi
> -	$(call quiet-command,texi2pdf $(TEXIFLAG) -I . $<,"GEN","$@")
> +	$(call quiet-command,texi2pdf $(TEXIFLAG) -I . $< -o $@,"GEN","$@")

I guess this simply makes the default implicit.  Okay.

> +
> +%.txt: %.texi
> +	$(call quiet-command,LC_ALL=C $(MAKEINFO) $(MAKEINFOFLAGS) --plaintext $< -o $@,\
> +	"  GEN   $@")

    $ make docs/qemu-qmp-ref.txt
        GEN   docs/qemu-qmp-ref.txt 
    Negative repeat count does nothing at /usr/share/texinfo/Texinfo/Convert/Line.pm line 128.
    Negative repeat count does nothing at /usr/share/texinfo/Texinfo/Convert/Line.pm line 128.
    Negative repeat count does nothing at /usr/share/texinfo/Texinfo/Convert/Line.pm line 128.

Could be something wrong with my system.  The result looks okay to me
all the same.

>  
>  qemu-options.texi: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool
>  	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@")
> @@ -556,6 +573,12 @@ qemu-monitor-info.texi: $(SRC_PATH)/hmp-commands-info.hx $(SRC_PATH)/scripts/hxt
>  qemu-img-cmds.texi: $(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/scripts/hxtool
>  	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@")
>  
> +qemu-qapi.texi: $(qapi-modules) $(qapi-py)
> +	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi2texi.py $< > $@,"GEN" "$@")
> +
> +qemu-ga-qapi.texi: $(SRC_PATH)/qga/qapi-schema.json $(qapi-py)
> +	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi2texi.py $< > $@,"GEN","$@")
> +
>  qemu.1: qemu-doc.texi qemu-options.texi qemu-monitor.texi qemu-monitor-info.texi
>  	$(call quiet-command, \
>  	  perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu.pod && \
> @@ -587,16 +610,35 @@ qemu-ga.8: qemu-ga.texi
>  	  $(POD2MAN) --section=8 --center=" " --release=" " qemu-ga.pod > $@, \
>  	  "GEN","$@")
>  
> -dvi: qemu-doc.dvi
> -html: qemu-doc.html
> -info: qemu-doc.info
> -pdf: qemu-doc.pdf
> +docs/qemu-qmp-ref.7:
> +	$(call quiet-command, \
> +	 perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu-qmp-ref.pod && \
> +	 $(POD2MAN) --section=7 --center=" " --release=" " qemu-qmp-ref.pod > $@, \
> +	 "GEN","$@")
> +
> +docs/qemu-ga-ref.7:
> +	$(call quiet-command, \
> +	 perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu-ga-ref.pod && \
> +	 $(POD2MAN) --section=7 --center=" " --release=" " qemu-ga-ref.pod > $@, \
> +	 "GEN","$@")

Recipe duplicated.  Would the following work?

   docs/qemu-qmp-ref.7 docs/qemu-ga-ref.7:
   	$(call quiet-command, \
   	 perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< $(@:.7=.pod) && \
   	 $(POD2MAN) --section=7 --center=" " --release=" " $(@:.7=.pod) > $@, \
   	 "GEN","$@")

Hmm, the recipe is duplicated many times over elsewhere already.
Nevermind, unless you genuinely want to clean that up.

> +
> +dvi: qemu-doc.dvi docs/qemu-qmp-ref.dvi docs/qemu-ga-ref.dvi
> +html: qemu-doc.html docs/qemu-qmp-ref.html docs/qemu-ga-ref.html
> +info: qemu-doc.info docs/qemu-qmp-ref.info docs/qemu-ga-ref.info
> +pdf: qemu-doc.pdf docs/qemu-qmp-ref.pdf docs/qemu-ga-ref.pdf

Would a txt target make sense?

>  
>  qemu-doc.dvi qemu-doc.html qemu-doc.info qemu-doc.pdf: \
>  	qemu-img.texi qemu-nbd.texi qemu-options.texi qemu-option-trace.texi \
>  	qemu-monitor.texi qemu-img-cmds.texi qemu-ga.texi \
>  	qemu-monitor-info.texi
>  
> +docs/qemu-ga-ref.dvi docs/qemu-ga-ref.html docs/qemu-ga-ref.info docs/qemu-ga-ref.pdf docs/qemu-ga-ref.txt docs/qemu-ga-ref.7: \
> +docs/qemu-ga-ref.texi qemu-ga-qapi.texi
> +
> +docs/qemu-qmp-ref.dvi docs/qemu-qmp-ref.html docs/qemu-qmp-ref.info docs/qemu-qmp-ref.pdf docs/qemu-qmp-ref.txt docs/qemu-qmp-ref.7: \
> +docs/qemu-qmp-ref.texi qemu-qapi.texi
> +
> +
>  ifdef CONFIG_WIN32
>  
>  INSTALLER = qemu-setup-$(VERSION)$(EXESUF)
> diff --git a/.gitignore b/.gitignore
> index 3d7848c..d0905c3 100644
> --- a/.gitignore
> +++ b/.gitignore
> @@ -39,7 +39,7 @@
>  /qmp-introspect.[ch]
>  /qmp-marshal.c
>  /qemu-doc.html
> -/qemu-doc.info
> +/qemu-doc.info*

I guess this is to cover the .info-1, .info-2, ... makeinfo produces
when it splits the output.  Separate patch, please.

Perhaps we should consider passing --no-split to makeinfo instead.  The
split I get seems rather pointless: .info-2 has a few indexes, all but
one empty, and .info-1 has all the rest, including some indexes.

>  /qemu-img
>  /qemu-nbd
>  /qemu-options.def
> @@ -109,6 +109,15 @@
>  /pc-bios/optionrom/kvmvapic.img
>  /pc-bios/s390-ccw/s390-ccw.elf
>  /pc-bios/s390-ccw/s390-ccw.img
> +/docs/qemu-ga-ref.html
> +/docs/qemu-ga-ref.txt
> +/docs/qemu-qmp-ref.html
> +/docs/qemu-qmp-ref.txt
> +docs/qemu-ga-ref.info*
> +docs/qemu-qmp-ref.info*
> +/qemu-ga-qapi.texi
> +/qemu-qapi.texi
> +*.tps
>  .stgit-*
>  cscope.*
>  tags
> diff --git a/docs/qmp-intro.txt b/docs/qmp-intro.txt
> index f6a3a03..60deafb 100644
> --- a/docs/qmp-intro.txt
> +++ b/docs/qmp-intro.txt
> @@ -16,8 +16,7 @@ QMP is JSON[1] based and features the following:
>  For detailed information on QMP's usage, please, refer to the following files:
>  
>  o qmp-spec.txt      QEMU Machine Protocol current specification
> -o qmp-commands.txt  QMP supported commands (auto-generated at build-time)
> -o qmp-events.txt    List of available asynchronous events
> +o qemu-qmp-ref.html QEMU QMP commands and events (auto-generated at build-time)
>  
>  [1] http://www.json.org

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v5 16/17] docs: add qemu logo
  2016-11-17 15:55 ` [Qemu-devel] [PATCH v5 16/17] docs: add qemu logo Marc-André Lureau
@ 2016-11-18 12:52   ` Markus Armbruster
  2016-11-21 10:50     ` Marc-André Lureau
  0 siblings, 1 reply; 48+ messages in thread
From: Markus Armbruster @ 2016-11-18 12:52 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

Marc-André Lureau <marcandre.lureau@redhat.com> writes:

> The pdf (needed by texi2pdf for vectorized images) was generated thanks
> to inkscape, from the pc-bios/qemu_logo.svg file.

Since we don't want to build-depend on inkscape, we need to commit the
.pdf.  Not that bad, as the .svg is unlikely to change.  But let's
explain that in the commit message.  Recommend to throw in the inkscape
command line.

>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

I'm afraid this fails when build tree != source tree.  I get

    $ make pdf
      GEN     docs/qemu-qmp-ref.pdf
    /usr/bin/texi2dvi: pdfetex exited with bad status, quitting.
    Makefile:558: recipe for target 'docs/qemu-qmp-ref.pdf' failed
    make: *** [docs/qemu-qmp-ref.pdf] Error 1
    $ tail qemu-qmp-ref.log 
                                                      @let @centersub @relax 
    l.12 @center @image{docs/qemu_logo}

    @Texinfo supports .png, .jpg, .jpeg, and .pdf images with PDF output, and none 
    of those formats could be found. (.eps cannot be supported due to the design of
     the PDF format; use regular TeX (DVI output) for that.) 


    !pdfTeX error: pdfetex (file docs/qemu_logo.): cannot find image file
     ==> Fatal error occurred, no output PDF file produced!

make dvi fails for me, too.

> ---
>  docs/qemu-ga-ref.texi  |   4 ++++
>  docs/qemu-qmp-ref.texi |   4 ++++
>  docs/qemu_logo.pdf     | Bin 0 -> 9117 bytes
>  3 files changed, 8 insertions(+)
>  create mode 100644 docs/qemu_logo.pdf
>
> diff --git a/docs/qemu-ga-ref.texi b/docs/qemu-ga-ref.texi
> index 02ecdb7..f7ed73e 100644
> --- a/docs/qemu-ga-ref.texi
> +++ b/docs/qemu-ga-ref.texi
> @@ -6,6 +6,10 @@
>  
>  @settitle QEMU Guest Agent Protocol Reference
>  
> +@iftex
> +@center @image{docs/qemu_logo}
> +@end iftex
> +
>  @copying
>  This is the QEMU Guest Agent Protocol reference manual.
>  
> diff --git a/docs/qemu-qmp-ref.texi b/docs/qemu-qmp-ref.texi
> index ccc03cb..0f7e9e4 100644
> --- a/docs/qemu-qmp-ref.texi
> +++ b/docs/qemu-qmp-ref.texi
> @@ -6,6 +6,10 @@
>  
>  @settitle QEMU QMP Reference Manual
>  
> +@iftex
> +@center @image{docs/qemu_logo}
> +@end iftex
> +

Quoting the Texinfo manual:

Here is the synopsis of the '@image' command:

     @image{FILENAME[, WIDTH[, HEIGHT[, ALTTEXT[, EXTENSION]]]]}

  The FILENAME argument is mandatory, and must not have an extension,
because the different processors support different formats:

   * TeX (DVI output) reads the file 'FILENAME.eps' (Encapsulated
     PostScript format).

   * pdfTeX reads 'FILENAME.pdf', 'FILENAME.png', 'FILENAME.jpg', or
     'FILENAME.jpeg' (in that order).  It also tries uppercase versions
     of the extensions.  The PDF format does not support EPS images, so
     such must be converted first.

   * For Info, 'makeinfo' includes 'FILENAME.txt' verbatim (more or less
     as if it were in '@verbatim').  The Info output may also include a
     reference to 'FILENAME.png' or 'FILENAME.jpg'.  (See below.)

   * For HTML, 'makeinfo' outputs a reference to 'FILENAME.png',
     'FILENAME.jpg', 'FILENAME.jpeg' or 'FILENAME.gif' (in that order).
     If none of those exist, it gives an error, and outputs a reference
     to 'FILENAME.jpg' anyway.

   * For Docbook, 'makeinfo' outputs references to 'FILENAME.eps',
     'FILENAME.gif' 'FILENAME.jpeg', 'FILENAME.jpg', 'FILENAME.pdf',
     'FILENAME.png' and 'FILENAME.svg', for every file found.  Also,
     'FILENAME.txt' is included verbatim, if present.  (The subsequent
     Docbook processor is supposed to choose the appropriate one.)

   * For Info and HTML output, 'makeinfo' uses the optional fifth
     argument EXTENSION to '@image' for the filename extension, if it is
     specified and the file is found.  Any leading period should be
     included in EXTENSION.  For example:

          @image{foo,,,,.xpm}

  If you want to install image files for use by Info readers too, we
recommend putting them in a subdirectory like 'FOO-figures' for a
package FOO.  Copying the files into '$(infodir)/FOO-figures/' should be
done in your 'Makefile'.

End quote.

You provide qemu_logo.pdf, which satisfies pdfTeX (our make target pdf)
and Docbook (we don't care).  Should we provide files to satisfy Info
and HTML?

My version of texinfo seems to ignore the @image when it can't file any
file in some cases.  That's why make info and make html still work.

>  @copying
>  This is the QEMU QMP reference manual.
>  
> diff --git a/docs/qemu_logo.pdf b/docs/qemu_logo.pdf
> new file mode 100644
> index 0000000000000000000000000000000000000000..294cb7dec50de73c786925671300fb0abdf9d641
> GIT binary patch
[...]

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v5 03/17] qga/schema: improve guest-set-vcpus Returns: section
  2016-11-18  8:49     ` Marc-André Lureau
@ 2016-11-18 14:07       ` Markus Armbruster
  0 siblings, 0 replies; 48+ messages in thread
From: Markus Armbruster @ 2016-11-18 14:07 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: Marc-André Lureau, qemu-devel

Marc-André Lureau <mlureau@redhat.com> writes:

> Hi
>
> ----- Original Message -----
>> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
>> 
>> > The documentation parser we are going to add finishes a section after an
>> > empty line.
>> 
>> Is this still true?
>
> No longer true, the parser only breaks on argument empty line, as explained in cover letter.
>
> I'll bring back the empty lines.

Feel free to keep them out, and justify your change with a consistency
argument.

[...]

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v5 12/17] qapi: rename QAPIExprError/QAPILineError
  2016-11-18 10:31     ` Marc-André Lureau
@ 2016-11-18 14:13       ` Markus Armbruster
  0 siblings, 0 replies; 48+ messages in thread
From: Markus Armbruster @ 2016-11-18 14:13 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: Marc-André Lureau, qemu-devel

Marc-André Lureau <mlureau@redhat.com> writes:

> ----- Original Message -----
>> Make the summary line "qapi: Rename QAPIExprError to QAPILineError".
>> 
>
> ok
>
>> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
>> 
>> > There is nothing specific about expressions in this exception,
>> > the following patch will use it without expressions.
>> >
>> > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>> > ---
>> >  scripts/qapi.py | 146
>> >  ++++++++++++++++++++++++++++----------------------------
>> >  1 file changed, 73 insertions(+), 73 deletions(-)
>> >
>> > diff --git a/scripts/qapi.py b/scripts/qapi.py
>> > index 21bc32f..4d1b0e4 100644
>> > --- a/scripts/qapi.py
>> > +++ b/scripts/qapi.py
>> > @@ -110,11 +110,11 @@ class QAPISchemaError(Exception):
>> >              "%s:%d:%d: %s" % (self.fname, self.line, self.col, self.msg)
>> >  
>> >  
>> > -class QAPIExprError(Exception):
>> > -    def __init__(self, expr_info, msg):
>> > +class QAPILineError(Exception):
>> > +    def __init__(self, info, msg):
>> >          Exception.__init__(self)
>> > -        assert expr_info
>> > -        self.info = expr_info
>> > +        assert info
>> > +        self.info = info
>> >          self.msg = msg
>> >  
>> >      def __str__(self):
>> 
>> Since we're talking about misnamed / awkward error stuff:
>> 
>> * QAPISchemaError is really a parse error.  __init__()'s schema argument
>>   isn't a QAPISchema, it's a QAPISchemaParser.
>> 
>> * Method __str__() is mostly duplicated.
>> 
>> How do you like the following untested sketch?
>
> I like it, though I would consider it separately from this series.

Punting ideas we both like to later series is fair.  But when we intend
to rework the error classes later, renaming one of them now is perhaps
not worth the churn.

> Here I systematically renamed the existing class ("line" since it's
> about location in file), your proposed change has more implications I
> don't really want to get into here.

Well, QAPISchemaError is just as much about "location in file" as
QAPIExprError.  They just specify the location differently: one by
reference to the parser's current location, and the other by an "info"
dictionary.

[...]

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v5 16/17] docs: add qemu logo
  2016-11-18 12:52   ` Markus Armbruster
@ 2016-11-21 10:50     ` Marc-André Lureau
  2016-11-21 12:07       ` Markus Armbruster
  0 siblings, 1 reply; 48+ messages in thread
From: Marc-André Lureau @ 2016-11-21 10:50 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel

Hi

On Fri, Nov 18, 2016 at 4:53 PM Markus Armbruster <armbru@redhat.com> wrote:

> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
>
> > The pdf (needed by texi2pdf for vectorized images) was generated thanks
> > to inkscape, from the pc-bios/qemu_logo.svg file.
>
> Since we don't want to build-depend on inkscape, we need to commit the
> .pdf.  Not that bad, as the .svg is unlikely to change.  But let's
> explain that in the commit message.  Recommend to throw in the inkscape
> command line.
>

ok


>
> >
> > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>
> I'm afraid this fails when build tree != source tree.  I get
>
>     $ make pdf
>       GEN     docs/qemu-qmp-ref.pdf
>     /usr/bin/texi2dvi: pdfetex exited with bad status, quitting.
>     Makefile:558: recipe for target 'docs/qemu-qmp-ref.pdf' failed
>     make: *** [docs/qemu-qmp-ref.pdf] Error 1
>     $ tail qemu-qmp-ref.log
>                                                       @let @centersub
> @relax
>     l.12 @center @image{docs/qemu_logo}
>
>     @Texinfo supports .png, .jpg, .jpeg, and .pdf images with PDF output,
> and none
>     of those formats could be found. (.eps cannot be supported due to the
> design of
>      the PDF format; use regular TeX (DVI output) for that.)
>
>
>     !pdfTeX error: pdfetex (file docs/qemu_logo.): cannot find image file
>      ==> Fatal error occurred, no output PDF file produced!
>
>
Adding -I $(SRC_PATH) solves it

make dvi fails for me, too.
>

I propose we drop dvi format rules (pdf is generally better supported)

>
> > ---
> >  docs/qemu-ga-ref.texi  |   4 ++++
> >  docs/qemu-qmp-ref.texi |   4 ++++
> >  docs/qemu_logo.pdf     | Bin 0 -> 9117 bytes
> >  3 files changed, 8 insertions(+)
> >  create mode 100644 docs/qemu_logo.pdf
> >
> > diff --git a/docs/qemu-ga-ref.texi b/docs/qemu-ga-ref.texi
> > index 02ecdb7..f7ed73e 100644
> > --- a/docs/qemu-ga-ref.texi
> > +++ b/docs/qemu-ga-ref.texi
> > @@ -6,6 +6,10 @@
> >
> >  @settitle QEMU Guest Agent Protocol Reference
> >
> > +@iftex
> > +@center @image{docs/qemu_logo}
> > +@end iftex
> > +
> >  @copying
> >  This is the QEMU Guest Agent Protocol reference manual.
> >
> > diff --git a/docs/qemu-qmp-ref.texi b/docs/qemu-qmp-ref.texi
> > index ccc03cb..0f7e9e4 100644
> > --- a/docs/qemu-qmp-ref.texi
> > +++ b/docs/qemu-qmp-ref.texi
> > @@ -6,6 +6,10 @@
> >
> >  @settitle QEMU QMP Reference Manual
> >
> > +@iftex
> > +@center @image{docs/qemu_logo}
> > +@end iftex
> > +
>
> Quoting the Texinfo manual:
>
> Here is the synopsis of the '@image' command:
>
>      @image{FILENAME[, WIDTH[, HEIGHT[, ALTTEXT[, EXTENSION]]]]}
>
>   The FILENAME argument is mandatory, and must not have an extension,
> because the different processors support different formats:
>
>    * TeX (DVI output) reads the file 'FILENAME.eps' (Encapsulated
>      PostScript format).
>
>    * pdfTeX reads 'FILENAME.pdf', 'FILENAME.png', 'FILENAME.jpg', or
>      'FILENAME.jpeg' (in that order).  It also tries uppercase versions
>      of the extensions.  The PDF format does not support EPS images, so
>      such must be converted first.
>
>    * For Info, 'makeinfo' includes 'FILENAME.txt' verbatim (more or less
>      as if it were in '@verbatim').  The Info output may also include a
>      reference to 'FILENAME.png' or 'FILENAME.jpg'.  (See below.)
>
>    * For HTML, 'makeinfo' outputs a reference to 'FILENAME.png',
>      'FILENAME.jpg', 'FILENAME.jpeg' or 'FILENAME.gif' (in that order).
>      If none of those exist, it gives an error, and outputs a reference
>      to 'FILENAME.jpg' anyway.
>
>    * For Docbook, 'makeinfo' outputs references to 'FILENAME.eps',
>      'FILENAME.gif' 'FILENAME.jpeg', 'FILENAME.jpg', 'FILENAME.pdf',
>      'FILENAME.png' and 'FILENAME.svg', for every file found.  Also,
>      'FILENAME.txt' is included verbatim, if present.  (The subsequent
>      Docbook processor is supposed to choose the appropriate one.)
>
>    * For Info and HTML output, 'makeinfo' uses the optional fifth
>      argument EXTENSION to '@image' for the filename extension, if it is
>      specified and the file is found.  Any leading period should be
>      included in EXTENSION.  For example:
>
>           @image{foo,,,,.xpm}
>
>   If you want to install image files for use by Info readers too, we
> recommend putting them in a subdirectory like 'FOO-figures' for a
> package FOO.  Copying the files into '$(infodir)/FOO-figures/' should be
> done in your 'Makefile'.
>
> End quote.
>
> You provide qemu_logo.pdf, which satisfies pdfTeX (our make target pdf)
> and Docbook (we don't care).  Should we provide files to satisfy Info
> and HTML?
>

The logo is only included @iftex.

I don't see much point in having the logo in info file.

We can add eventually add it to html, I guess it depends how the html will
be published, I'd rather fix that when we improve our doc toolchain in the
next release (I suppose the pdf will still be generated with texi2pdf, but
the html with sphinx with Paolo texi/docbook integration)


> My version of texinfo seems to ignore the @image when it can't file any
> file in some cases.  That's why make info and make html still work.
>
> >  @copying
> >  This is the QEMU QMP reference manual.
> >
> > diff --git a/docs/qemu_logo.pdf b/docs/qemu_logo.pdf
> > new file mode 100644
> > index
> 0000000000000000000000000000000000000000..294cb7dec50de73c786925671300fb0abdf9d641
> > GIT binary patch
> [...]
>
> --
Marc-André Lureau

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v5 16/17] docs: add qemu logo
  2016-11-21 10:50     ` Marc-André Lureau
@ 2016-11-21 12:07       ` Markus Armbruster
  0 siblings, 0 replies; 48+ messages in thread
From: Markus Armbruster @ 2016-11-21 12:07 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

Marc-André Lureau <marcandre.lureau@gmail.com> writes:

> Hi
>
> On Fri, Nov 18, 2016 at 4:53 PM Markus Armbruster <armbru@redhat.com> wrote:
>
>> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
>>
>> > The pdf (needed by texi2pdf for vectorized images) was generated thanks
>> > to inkscape, from the pc-bios/qemu_logo.svg file.
>>
>> Since we don't want to build-depend on inkscape, we need to commit the
>> .pdf.  Not that bad, as the .svg is unlikely to change.  But let's
>> explain that in the commit message.  Recommend to throw in the inkscape
>> command line.
>>
>
> ok
>
>
>>
>> >
>> > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>>
>> I'm afraid this fails when build tree != source tree.  I get
>>
>>     $ make pdf
>>       GEN     docs/qemu-qmp-ref.pdf
>>     /usr/bin/texi2dvi: pdfetex exited with bad status, quitting.
>>     Makefile:558: recipe for target 'docs/qemu-qmp-ref.pdf' failed
>>     make: *** [docs/qemu-qmp-ref.pdf] Error 1
>>     $ tail qemu-qmp-ref.log
>>                                                       @let @centersub
>> @relax
>>     l.12 @center @image{docs/qemu_logo}
>>
>>     @Texinfo supports .png, .jpg, .jpeg, and .pdf images with PDF output,
>> and none
>>     of those formats could be found. (.eps cannot be supported due to the
>> design of
>>      the PDF format; use regular TeX (DVI output) for that.)
>>
>>
>>     !pdfTeX error: pdfetex (file docs/qemu_logo.): cannot find image file
>>      ==> Fatal error occurred, no output PDF file produced!
>>
>>
> Adding -I $(SRC_PATH) solves it
>
> make dvi fails for me, too.
>>
>
> I propose we drop dvi format rules (pdf is generally better supported)

I figure DVI doesn't provide all that much value anymore, so dropping it
could be justified if keeping it is a burden.  Separate patch, of
course.

>> > ---
>> >  docs/qemu-ga-ref.texi  |   4 ++++
>> >  docs/qemu-qmp-ref.texi |   4 ++++
>> >  docs/qemu_logo.pdf     | Bin 0 -> 9117 bytes
>> >  3 files changed, 8 insertions(+)
>> >  create mode 100644 docs/qemu_logo.pdf
>> >
>> > diff --git a/docs/qemu-ga-ref.texi b/docs/qemu-ga-ref.texi
>> > index 02ecdb7..f7ed73e 100644
>> > --- a/docs/qemu-ga-ref.texi
>> > +++ b/docs/qemu-ga-ref.texi
>> > @@ -6,6 +6,10 @@
>> >
>> >  @settitle QEMU Guest Agent Protocol Reference
>> >
>> > +@iftex
>> > +@center @image{docs/qemu_logo}
>> > +@end iftex
>> > +
>> >  @copying
>> >  This is the QEMU Guest Agent Protocol reference manual.
>> >
>> > diff --git a/docs/qemu-qmp-ref.texi b/docs/qemu-qmp-ref.texi
>> > index ccc03cb..0f7e9e4 100644
>> > --- a/docs/qemu-qmp-ref.texi
>> > +++ b/docs/qemu-qmp-ref.texi
>> > @@ -6,6 +6,10 @@
>> >
>> >  @settitle QEMU QMP Reference Manual
>> >
>> > +@iftex
>> > +@center @image{docs/qemu_logo}
>> > +@end iftex
>> > +
>>
>> Quoting the Texinfo manual:
>>
>> Here is the synopsis of the '@image' command:
>>
>>      @image{FILENAME[, WIDTH[, HEIGHT[, ALTTEXT[, EXTENSION]]]]}
>>
>>   The FILENAME argument is mandatory, and must not have an extension,
>> because the different processors support different formats:
>>
>>    * TeX (DVI output) reads the file 'FILENAME.eps' (Encapsulated
>>      PostScript format).
>>
>>    * pdfTeX reads 'FILENAME.pdf', 'FILENAME.png', 'FILENAME.jpg', or
>>      'FILENAME.jpeg' (in that order).  It also tries uppercase versions
>>      of the extensions.  The PDF format does not support EPS images, so
>>      such must be converted first.
>>
>>    * For Info, 'makeinfo' includes 'FILENAME.txt' verbatim (more or less
>>      as if it were in '@verbatim').  The Info output may also include a
>>      reference to 'FILENAME.png' or 'FILENAME.jpg'.  (See below.)
>>
>>    * For HTML, 'makeinfo' outputs a reference to 'FILENAME.png',
>>      'FILENAME.jpg', 'FILENAME.jpeg' or 'FILENAME.gif' (in that order).
>>      If none of those exist, it gives an error, and outputs a reference
>>      to 'FILENAME.jpg' anyway.
>>
>>    * For Docbook, 'makeinfo' outputs references to 'FILENAME.eps',
>>      'FILENAME.gif' 'FILENAME.jpeg', 'FILENAME.jpg', 'FILENAME.pdf',
>>      'FILENAME.png' and 'FILENAME.svg', for every file found.  Also,
>>      'FILENAME.txt' is included verbatim, if present.  (The subsequent
>>      Docbook processor is supposed to choose the appropriate one.)
>>
>>    * For Info and HTML output, 'makeinfo' uses the optional fifth
>>      argument EXTENSION to '@image' for the filename extension, if it is
>>      specified and the file is found.  Any leading period should be
>>      included in EXTENSION.  For example:
>>
>>           @image{foo,,,,.xpm}
>>
>>   If you want to install image files for use by Info readers too, we
>> recommend putting them in a subdirectory like 'FOO-figures' for a
>> package FOO.  Copying the files into '$(infodir)/FOO-figures/' should be
>> done in your 'Makefile'.
>>
>> End quote.
>>
>> You provide qemu_logo.pdf, which satisfies pdfTeX (our make target pdf)
>> and Docbook (we don't care).  Should we provide files to satisfy Info
>> and HTML?
>>
>
> The logo is only included @iftex.
>
> I don't see much point in having the logo in info file.

Same point as for PDF: it looks pretty.  At least in sufficiently
capable Info readers:

      In Info output, 'makeinfo' writes a reference to the binary image file
    (trying FILENAME suffixed with 'EXTENSION', '.EXTENSION', '.png', or
    '.jpg', in that order) if one exists.  It also literally includes the
    '.txt' file if one exists.  This way, Info readers which can display
    images (such as the Emacs Info browser, running under X) can do so,
    whereas Info readers which can only use text (such as the standalone
    Info reader) can display the textual version.

Not implementing the logo for Info isn't a deal-breaker, of course.

> We can add eventually add it to html, I guess it depends how the html will
> be published, I'd rather fix that when we improve our doc toolchain in the
> next release (I suppose the pdf will still be generated with texi2pdf, but
> the html with sphinx with Paolo texi/docbook integration)

Not a deal-breaker, either.

Limitations should be mentioned in the commit message, and perhaps in
comments.

>> My version of texinfo seems to ignore the @image when it can't file any
>> file in some cases.  That's why make info and make html still work.
>>
>> >  @copying
>> >  This is the QEMU QMP reference manual.
>> >
>> > diff --git a/docs/qemu_logo.pdf b/docs/qemu_logo.pdf
>> > new file mode 100644
>> > index
>> 0000000000000000000000000000000000000000..294cb7dec50de73c786925671300fb0abdf9d641
>> > GIT binary patch
>> [...]
>>
>> --
> Marc-André Lureau

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v5 17/17] build-sys: add qapi doc generation targets
  2016-11-18 12:31   ` Markus Armbruster
@ 2016-11-21 12:30     ` Marc-André Lureau
  0 siblings, 0 replies; 48+ messages in thread
From: Marc-André Lureau @ 2016-11-21 12:30 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel

Hi

On Fri, Nov 18, 2016 at 4:32 PM Markus Armbruster <armbru@redhat.com> wrote:

> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
>
> > Generate and install the man and html version of QAPI documentation.
> >
> > Add it also to optional pdf/dvi/info targets.
> >
> > Also support plain-text targets docs/qemu-ga-ref.txt &
> docs/qemu-qmp-ref.txt.
> >
> > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> > ---
> >  Makefile           | 56
> +++++++++++++++++++++++++++++++++++++++++++++++-------
> >  .gitignore         | 11 ++++++++++-
> >  docs/qmp-intro.txt |  3 +--
> >  3 files changed, 60 insertions(+), 10 deletions(-)
> >
> > diff --git a/Makefile b/Makefile
> > index 3617736..cc1c46e 100644
> > --- a/Makefile
> > +++ b/Makefile
> > @@ -91,6 +91,8 @@ HELPERS-$(CONFIG_LINUX) = qemu-bridge-helper$(EXESUF)
> >
> >  ifdef BUILD_DOCS
> >  DOCS=qemu-doc.html qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8
> > +DOCS+=docs/qemu-qmp-ref.html docs/qemu-qmp-ref.7
> > +DOCS+=docs/qemu-ga-ref.html docs/qemu-ga-ref.7
> >  ifdef CONFIG_VIRTFS
> >  DOCS+=fsdev/virtfs-proxy-helper.1
> >  endif
> > @@ -266,6 +268,7 @@ qemu-ga$(EXESUF): QEMU_CFLAGS += -I
> qga/qapi-generated
> >  gen-out-type = $(subst .,-,$(suffix $@))
> >
> >  qapi-py = $(SRC_PATH)/scripts/qapi.py $(SRC_PATH)/scripts/ordereddict.py
> > +qapi-py += $(SRC_PATH)/scripts/qapi2texi.py
> >
> >  qga/qapi-generated/qga-qapi-types.c qga/qapi-generated/qga-qapi-types.h
> :\
> >  $(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-types.py
> $(qapi-py)
> > @@ -395,6 +398,11 @@ distclean: clean
> >       rm -f qemu-doc.vr
> >       rm -f config.log
> >       rm -f linux-headers/asm
> > +     rm -f qemu-ga-qapi.texi qemu-qapi.texi
> > +     rm -f docs/qemu-qmp-ref.7 docs/qemu-ga-ref.7
> > +     rm -f docs/qemu-qmp-ref.txt docs/qemu-ga-ref.txt
> > +     rm -f docs/qemu-qmp-ref.pdf docs/qemu-ga-ref.pdf
> > +     rm -f docs/qemu-qmp-ref.html docs/qemu-ga-ref.html
>
> Missing: .dvi
>

I dropped it in a previous patch in the new series


>
> >       for d in $(TARGET_DIRS); do \
> >       rm -rf $$d || exit 1 ; \
> >          done
> > @@ -431,9 +439,12 @@ endif
> >  install-doc: $(DOCS)
> >       $(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)"
> >       $(INSTALL_DATA) qemu-doc.html "$(DESTDIR)$(qemu_docdir)"
> > +     $(INSTALL_DATA) docs/qemu-qmp-ref.html "$(DESTDIR)$(qemu_docdir)"
>
> Personally, I'd install .txt along with .html, because the .txt is
> perfectly legible, and plain text is easier to read in some situations.
> We don't do that for qemu-doc, though.
>

Ok, let's install them all

>
> >  ifdef CONFIG_POSIX
> >       $(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1"
> >       $(INSTALL_DATA) qemu.1 "$(DESTDIR)$(mandir)/man1"
> > +     $(INSTALL_DIR) "$(DESTDIR)$(mandir)/man7"
> > +     $(INSTALL_DATA) docs/qemu-qmp-ref.7 "$(DESTDIR)$(mandir)/man7"
> >  ifneq ($(TOOLS),)
> >       $(INSTALL_DATA) qemu-img.1 "$(DESTDIR)$(mandir)/man1"
> >       $(INSTALL_DIR) "$(DESTDIR)$(mandir)/man8"
> > @@ -441,6 +452,8 @@ ifneq ($(TOOLS),)
> >  endif
> >  ifneq (,$(findstring qemu-ga,$(TOOLS)))
> >       $(INSTALL_DATA) qemu-ga.8 "$(DESTDIR)$(mandir)/man8"
> > +     $(INSTALL_DATA) docs/qemu-ga-ref.html "$(DESTDIR)$(qemu_docdir)"
> > +     $(INSTALL_DATA) docs/qemu-ga-ref.7 "$(DESTDIR)$(mandir)/man7"
> >  endif
> >  endif
> >  ifdef CONFIG_VIRTFS
> > @@ -528,9 +541,9 @@ ui/console-gl.o: $(SRC_PATH)/ui/console-gl.c \
> >       ui/shader/texture-blit-vert.h ui/shader/texture-blit-frag.h
> >
> >  # documentation
> > -MAKEINFO=makeinfo
> > +MAKEINFO=makeinfo -D 'VERSION $(VERSION)'
> >  MAKEINFOFLAGS=--no-headers --no-split --number-sections
> > -TEXIFLAG=$(if $(V),,--quiet)
> > +TEXIFLAG=$(if $(V),,--quiet) --command='@set VERSION $(VERSION)'
> >  %.dvi: %.texi
> >       $(call quiet-command,texi2dvi $(TEXIFLAG) -I . $<,"GEN","$@")
> >
>
> Defines variable VERSION for all .texi, not just qemu-*-ref.texi, for
> simplicity.  Works for me.
>
> > @@ -542,7 +555,11 @@ TEXIFLAG=$(if $(V),,--quiet)
> >       $(call quiet-command,$(MAKEINFO) $< -o $@,"GEN","$@")
> >
> >  %.pdf: %.texi
> > -     $(call quiet-command,texi2pdf $(TEXIFLAG) -I . $<,"GEN","$@")
> > +     $(call quiet-command,texi2pdf $(TEXIFLAG) -I . $< -o $@,"GEN","$@")
>
> I guess this simply makes the default implicit.  Okay.
>
> > +
> > +%.txt: %.texi
> > +     $(call quiet-command,LC_ALL=C $(MAKEINFO) $(MAKEINFOFLAGS)
> --plaintext $< -o $@,\
> > +     "  GEN   $@")
>
>     $ make docs/qemu-qmp-ref.txt
>         GEN   docs/qemu-qmp-ref.txt
>     Negative repeat count does nothing at
> /usr/share/texinfo/Texinfo/Convert/Line.pm line 128.
>     Negative repeat count does nothing at
> /usr/share/texinfo/Texinfo/Convert/Line.pm line 128.
>     Negative repeat count does nothing at
> /usr/share/texinfo/Texinfo/Convert/Line.pm line 128.
>
> Could be something wrong with my system.  The result looks okay to me
> all the same.
>

I have the same warning, seems to be harmless though...


>
> >
> >  qemu-options.texi: $(SRC_PATH)/qemu-options.hx
> $(SRC_PATH)/scripts/hxtool
> >       $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< >
> $@,"GEN","$@")
> > @@ -556,6 +573,12 @@ qemu-monitor-info.texi:
> $(SRC_PATH)/hmp-commands-info.hx $(SRC_PATH)/scripts/hxt
> >  qemu-img-cmds.texi: $(SRC_PATH)/qemu-img-cmds.hx
> $(SRC_PATH)/scripts/hxtool
> >       $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< >
> $@,"GEN","$@")
> >
> > +qemu-qapi.texi: $(qapi-modules) $(qapi-py)
> > +     $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi2texi.py $<
> > $@,"GEN" "$@")
> > +
> > +qemu-ga-qapi.texi: $(SRC_PATH)/qga/qapi-schema.json $(qapi-py)
> > +     $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi2texi.py $<
> > $@,"GEN","$@")
> > +
> >  qemu.1: qemu-doc.texi qemu-options.texi qemu-monitor.texi
> qemu-monitor-info.texi
> >       $(call quiet-command, \
> >         perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu.pod && \
> > @@ -587,16 +610,35 @@ qemu-ga.8: qemu-ga.texi
> >         $(POD2MAN) --section=8 --center=" " --release=" " qemu-ga.pod >
> $@, \
> >         "GEN","$@")
> >
> > -dvi: qemu-doc.dvi
> > -html: qemu-doc.html
> > -info: qemu-doc.info
> > -pdf: qemu-doc.pdf
> > +docs/qemu-qmp-ref.7:
> > +     $(call quiet-command, \
> > +      perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu-qmp-ref.pod
> && \
> > +      $(POD2MAN) --section=7 --center=" " --release=" "
> qemu-qmp-ref.pod > $@, \
> > +      "GEN","$@")
> > +
> > +docs/qemu-ga-ref.7:
> > +     $(call quiet-command, \
> > +      perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu-ga-ref.pod
> && \
> > +      $(POD2MAN) --section=7 --center=" " --release=" " qemu-ga-ref.pod
> > $@, \
> > +      "GEN","$@")
>
> Recipe duplicated.  Would the following work?
>
>    docs/qemu-qmp-ref.7 docs/qemu-ga-ref.7:
>         $(call quiet-command, \
>          perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< $(@:.7=.pod) && \
>          $(POD2MAN) --section=7 --center=" " --release=" " $(@:.7=.pod) >
> $@, \
>          "GEN","$@")
>
> Hmm, the recipe is duplicated many times over elsewhere already.
> Nevermind, unless you genuinely want to clean that up.
>

Fixed it in the new series

>
> > +
> > +dvi: qemu-doc.dvi docs/qemu-qmp-ref.dvi docs/qemu-ga-ref.dvi
> > +html: qemu-doc.html docs/qemu-qmp-ref.html docs/qemu-ga-ref.html
> > +info: qemu-doc.info docs/qemu-qmp-ref.info docs/qemu-ga-ref.info
> > +pdf: qemu-doc.pdf docs/qemu-qmp-ref.pdf docs/qemu-ga-ref.pdf
>
> Would a txt target make sense?
>

added


>
> >
> >  qemu-doc.dvi qemu-doc.html qemu-doc.info qemu-doc.pdf: \
> >       qemu-img.texi qemu-nbd.texi qemu-options.texi
> qemu-option-trace.texi \
> >       qemu-monitor.texi qemu-img-cmds.texi qemu-ga.texi \
> >       qemu-monitor-info.texi
> >
> > +docs/qemu-ga-ref.dvi docs/qemu-ga-ref.html docs/qemu-ga-ref.info
> docs/qemu-ga-ref.pdf docs/qemu-ga-ref.txt docs/qemu-ga-ref.7: \
> > +docs/qemu-ga-ref.texi qemu-ga-qapi.texi
> > +
> > +docs/qemu-qmp-ref.dvi docs/qemu-qmp-ref.html docs/qemu-qmp-ref.info
> docs/qemu-qmp-ref.pdf docs/qemu-qmp-ref.txt docs/qemu-qmp-ref.7: \
> > +docs/qemu-qmp-ref.texi qemu-qapi.texi
> > +
> > +
> >  ifdef CONFIG_WIN32
> >
> >  INSTALLER = qemu-setup-$(VERSION)$(EXESUF)
> > diff --git a/.gitignore b/.gitignore
> > index 3d7848c..d0905c3 100644
> > --- a/.gitignore
> > +++ b/.gitignore
> > @@ -39,7 +39,7 @@
> >  /qmp-introspect.[ch]
> >  /qmp-marshal.c
> >  /qemu-doc.html
> > -/qemu-doc.info
> > +/qemu-doc.info*
>
> I guess this is to cover the .info-1, .info-2, ... makeinfo produces
> when it splits the output.  Separate patch, please.
>
> ok


> Perhaps we should consider passing --no-split to makeinfo instead.  The
> split I get seems rather pointless: .info-2 has a few indexes, all but
> one empty, and .info-1 has all the rest, including some indexes.
>
> done


> >  /qemu-img
> >  /qemu-nbd
> >  /qemu-options.def
> > @@ -109,6 +109,15 @@
> >  /pc-bios/optionrom/kvmvapic.img
> >  /pc-bios/s390-ccw/s390-ccw.elf
> >  /pc-bios/s390-ccw/s390-ccw.img
> > +/docs/qemu-ga-ref.html
> > +/docs/qemu-ga-ref.txt
> > +/docs/qemu-qmp-ref.html
> > +/docs/qemu-qmp-ref.txt
> > +docs/qemu-ga-ref.info*
> > +docs/qemu-qmp-ref.info*
> > +/qemu-ga-qapi.texi
> > +/qemu-qapi.texi
> > +*.tps
> >  .stgit-*
> >  cscope.*
> >  tags
> > diff --git a/docs/qmp-intro.txt b/docs/qmp-intro.txt
> > index f6a3a03..60deafb 100644
> > --- a/docs/qmp-intro.txt
> > +++ b/docs/qmp-intro.txt
> > @@ -16,8 +16,7 @@ QMP is JSON[1] based and features the following:
> >  For detailed information on QMP's usage, please, refer to the following
> files:
> >
> >  o qmp-spec.txt      QEMU Machine Protocol current specification
> > -o qmp-commands.txt  QMP supported commands (auto-generated at
> build-time)
> > -o qmp-events.txt    List of available asynchronous events
> > +o qemu-qmp-ref.html QEMU QMP commands and events (auto-generated at
> build-time)
> >
> >  [1] http://www.json.org
>
> --
Marc-André Lureau

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v5 09/17] qapi: add some sections in docs
  2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 09/17] qapi: add some sections in docs Marc-André Lureau
  2016-11-17 17:43   ` Markus Armbruster
@ 2016-11-30 15:38   ` Markus Armbruster
  2016-11-30 16:07     ` Marc-André Lureau
  1 sibling, 1 reply; 48+ messages in thread
From: Markus Armbruster @ 2016-11-30 15:38 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

Second thoughts...

Marc-André Lureau <marcandre.lureau@redhat.com> writes:

> Add some more section titles to organize the produced documents.
>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  qapi-schema.json     |  4 ++++
>  qapi/block-core.json |  6 ++++--
>  qapi/block.json      | 10 ++++++++--
>  qapi/common.json     |  6 ++++--
>  qapi/crypto.json     |  5 ++++-
>  qapi/event.json      |  6 ++++++
>  qapi/rocker.json     |  4 ++++
>  qapi/trace.json      |  3 +++
>  8 files changed, 37 insertions(+), 7 deletions(-)
>
> diff --git a/qapi-schema.json b/qapi-schema.json
> index dfe68ba..69340f2 100644
> --- a/qapi-schema.json
> +++ b/qapi-schema.json
> @@ -20,6 +20,10 @@
>  # QAPI introspection
>  { 'include': 'qapi/introspect.json' }
>  
> +##
> +# = QMP commands
> +##
> +
>  ##
>  # @qmp_capabilities:
>  #
> diff --git a/qapi/block-core.json b/qapi/block-core.json
> index ec1da2a..05cedc3 100644
> --- a/qapi/block-core.json
> +++ b/qapi/block-core.json
> @@ -1,6 +1,8 @@
>  # -*- Mode: Python -*-
> -#
> -# QAPI block core definitions (vm unrelated)
> +
> +##
> +# == QAPI block core definitions (vm unrelated)
> +##
>  
>  # QAPI common definitions
>  { 'include': 'common.json' }
> diff --git a/qapi/block.json b/qapi/block.json
> index 937df05..e4ad74b 100644
> --- a/qapi/block.json
> +++ b/qapi/block.json
> @@ -1,10 +1,16 @@
>  # -*- Mode: Python -*-
> -#
> -# QAPI block definitions (vm related)
> +
> +##
> +# = QAPI block definitions
> +##
>  
>  # QAPI block core definitions
>  { 'include': 'block-core.json' }
>  
> +##
> +# == QAPI block definitions (vm unrelated)
> +##
> +
>  ##
>  # @BiosAtaTranslation:
>  #
> diff --git a/qapi/common.json b/qapi/common.json
> index 624a861..d93f159 100644
> --- a/qapi/common.json
> +++ b/qapi/common.json
> @@ -1,6 +1,8 @@
>  # -*- Mode: Python -*-
> -#
> -# QAPI common definitions
> +
> +##
> +# = QAPI common definitions
> +##
>  
>  ##
>  # @QapiErrorClass:
> diff --git a/qapi/crypto.json b/qapi/crypto.json
> index 15d296e..1e517b0 100644
> --- a/qapi/crypto.json
> +++ b/qapi/crypto.json
> @@ -1,6 +1,9 @@
>  # -*- Mode: Python -*-
>  #
> -# QAPI crypto definitions
> +
> +##
> +# = QAPI crypto definitions
> +##
>  
>  ##
>  # @QCryptoTLSCredsEndpoint:
> diff --git a/qapi/event.json b/qapi/event.json
> index 37bf34e..59942b0 100644
> --- a/qapi/event.json
> +++ b/qapi/event.json
> @@ -1,3 +1,9 @@
> +# -*- Mode: Python -*-
> +
> +##
> +# = Events
> +##
> +

This suggests *all* events are in this section, which isn't the case.
"Other events"?  "Core events"?  Dunno...

>  ##
>  # @SHUTDOWN:
>  #
> diff --git a/qapi/rocker.json b/qapi/rocker.json
> index ace2776..dd72e02 100644
> --- a/qapi/rocker.json
> +++ b/qapi/rocker.json
> @@ -1,4 +1,8 @@
>  ##
> +# = Rocker API

What about "Rocker switch device"?

> +##
> +
> +##
>  # @RockerSwitch:
>  #
>  # Rocker switch information.
> diff --git a/qapi/trace.json b/qapi/trace.json
> index 4fd39b7..3ad7df7 100644
> --- a/qapi/trace.json
> +++ b/qapi/trace.json
> @@ -5,6 +5,9 @@
>  # This work is licensed under the terms of the GNU GPL, version 2 or later.
>  # See the COPYING file in the top-level directory.
>  
> +##
> +# = Tracing commands
> +##
>  
>  ##
>  # @TraceEventState:

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v5 13/17] qapi: add qapi2texi script
  2016-11-17 15:55 ` [Qemu-devel] [PATCH v5 13/17] qapi: add qapi2texi script Marc-André Lureau
@ 2016-11-30 16:06   ` Markus Armbruster
  2016-12-05 17:35     ` Marc-André Lureau
  0 siblings, 1 reply; 48+ messages in thread
From: Markus Armbruster @ 2016-11-30 16:06 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

Marc-André Lureau <marcandre.lureau@redhat.com> writes:

> As the name suggests, the qapi2texi script converts JSON QAPI
> description into a texi file suitable for different target
> formats (info/man/txt/pdf/html...).
>
> It parses the following kind of blocks:
>
> Free-form:
>
>   ##
>   # = Section
>   # == Subsection
>   #
>   # Some text foo with *emphasis*
>   # 1. with a list
>   # 2. like that
>   #
>   # And some code:
>   # | $ echo foo
>   # | -> do this
>   # | <- get that
>   #
>   ##
>
> Symbol:
>
>   ##
>   # @symbol:
>   #
>   # Symbol body ditto ergo sum. Foo bar
>   # baz ding.
>   #
>   # @arg: foo
>   # @arg: #optional foo

Let's not use @arg twice.

Terminology: I prefer to use "parameter" for formal parameters, and
"argument" for actual arguments.  This matches how The Python Language
Reference uses the terms.

What about

    # @param1: the frob to frobnicate
    # @param2: #optional how hard to frobnicate

>   #
>   # Returns: returns bla bla
>   #          Or bla blah

Repeating "returns" is awkward, and we don't do that in our schemas.

We need a period before "Or", or spell it "or".

What about

    # Returns: the frobnicated frob.
    #          If frob isn't frobnicatable, GenericError.

>   #
>   # Since: version
>   # Notes: notes, comments can have
>   #        - itemized list
>   #        - like this
>   #
>   # Example:
>   #
>   # -> { "execute": "quit" }
>   # <- { "return": {} }
>   #
>   ##
>
> That's roughly following the following EBNF grammar:
>
> api_comment = "##\n" comment "##\n"
> comment = freeform_comment | symbol_comment
> freeform_comment = { "# " text "\n" | "#\n" }
> symbol_comment = "# @" name ":\n" { member | meta | freeform_comment }

Rejects non-empty comments where "#" is not followed by space.  Such
usage is accepted outside doc comments.  Hmm.

> member = "# @" name ':' [ text ] freeform_comment

Are you missing a "\n" before freeform_comment?

> meta = "# " ( "Returns:", "Since:", "Note:", "Notes:", "Example:", "Examples:" ) [ text ] freeform_comment

Likewise.

> text = free-text markdown-like, "#optional" for members

The grammar is ambiguous: a line "# @foo:\n" can be parsed both as
freeform_comment and as symbol_comment.  Since your intent is obvious
enough, it can still serve as documentation.  It's not a suitable
foundation for parsing, though.  Okay for a commit message.

> Thanks to the following json expressions, the documentation is enhanced
> with extra information about the type of arguments and return value
> expected.

I guess you want to say that we enrich the documentation we extract from
comments with information from the actual schema.  Correct?

Missing: a brief discussion of deficiencies.  These include:

* The generated QMP documentation includes internal types

  We use qapi-schema.json both for defining the external QMP interface
  and for defining internal types.  qmp-introspect.py carefully
  separates the two, to not expose internal types.  qapi2texi.py happily
  exposes everything.

  Currently, about a fifth of the types in the generated docs are
  internal:

      AcpiTableOptions
      BiosAtaTranslation
      BlockDeviceMapEntry
      COLOMessage
      COLOMode
      DummyForceArrays
      FailoverStatus
      FloppyDriveType
      ImageCheck
      LostTickPolicy
      MapEntry
      MigrationParameter
      NetClientDriver
      NetFilterDirection
      NetLegacy
      NetLegacyNicOptions
      NetLegacyOptions
      NetLegacyOptionsKind
      Netdev
      NetdevBridgeOptions
      NetdevDumpOptions
      NetdevHubPortOptions
      NetdevL2TPv3Options
      NetdevNetmapOptions
      NetdevNoneOptions
      NetdevSocketOptions
      NetdevTapOptions
      NetdevUserOptions
      NetdevVdeOptions
      NetdevVhostUserOptions
      NumaNodeOptions
      NumaOptions
      NumaOptionsKind
      OnOffAuto
      OnOffSplit
      PreallocMode
      QCryptoBlockCreateOptions
      QCryptoBlockCreateOptionsLUKS
      QCryptoBlockFormat
      QCryptoBlockInfo
      QCryptoBlockInfoBase
      QCryptoBlockInfoQCow
      QCryptoBlockOpenOptions
      QCryptoBlockOptionsBase
      QCryptoBlockOptionsLUKS
      QCryptoBlockOptionsQCow
      QCryptoSecretFormat
      QCryptoTLSCredsEndpoint
      QapiErrorClass
      ReplayMode
      X86CPUFeatureWordInfo
      X86CPURegister32

  Generating documentation for internal types might be useful, but
  letting them pollute QMP interface documentation isn't.  Needs fixing
  before we release.  Until then, needs a FIXME comment in qapi2texi.py.

* Union support is lacking

  The doc string language is adequate for documenting commands, events,
  and non-union types.  It doesn't really handle union variants.  Hardly
  surprising, as you fitted the language do existing practice, and
  existing (mal-)practice is neglecting to document union variant
  members.

* Documentation is lacking

  See review of qapi-code-gen.txt below.

* Doc comment error message positions are imprecise

  They always point to the beginning of the comment.

* Probably more

  We should update this with noteworthy findings during review.  I
  tried, but I suspect I missed some.

> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
[Lengthy diffstat snipped...]
>
> diff --git a/tests/Makefile.include b/tests/Makefile.include
> index e98d3b6..f16764c 100644
> --- a/tests/Makefile.include
> +++ b/tests/Makefile.include
> @@ -350,6 +350,21 @@ qapi-schema += base-cycle-direct.json
>  qapi-schema += base-cycle-indirect.json
>  qapi-schema += command-int.json
>  qapi-schema += comments.json
> +qapi-schema += doc-bad-args.json
> +qapi-schema += doc-bad-symbol.json
> +qapi-schema += doc-duplicated-arg.json
> +qapi-schema += doc-duplicated-return.json
> +qapi-schema += doc-duplicated-since.json
> +qapi-schema += doc-empty-arg.json
> +qapi-schema += doc-empty-section.json
> +qapi-schema += doc-empty-symbol.json
> +qapi-schema += doc-invalid-end.json
> +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-expr.json
> +qapi-schema += doc-missing-space.json
>  qapi-schema += double-data.json
>  qapi-schema += double-type.json
>  qapi-schema += duplicate-key.json
> @@ -443,6 +458,8 @@ qapi-schema += union-optional-branch.json
>  qapi-schema += union-unknown.json
>  qapi-schema += unknown-escape.json
>  qapi-schema += unknown-expr-key.json
> +
> +
>  check-qapi-schema-y := $(addprefix tests/qapi-schema/, $(qapi-schema))
>  
>  GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h \
> diff --git a/scripts/qapi.py b/scripts/qapi.py
> index 4d1b0e4..1b456b4 100644
> --- a/scripts/qapi.py
> +++ b/scripts/qapi.py
> @@ -122,6 +122,109 @@ class QAPILineError(Exception):
>              "%s:%d: %s" % (self.info['file'], self.info['line'], self.msg)
>  
>  
> +class QAPIDoc(object):
> +    class Section(object):
> +        def __init__(self, name=""):

name=None feels more natural than "" for an absent optional name.


> +            # optional section name (argument/member or section name)
> +            self.name = name
> +            # the list of strings for this section

Would "list of lines" be more accurate, or less?

> +            self.content = []
> +
> +        def append(self, line):
> +            self.content.append(line)
> +
> +        def __repr__(self):
> +            return "\n".join(self.content).strip()
> +
> +    class ArgSection(Section):
> +        pass

Begs the question what makes ArgSection differ from Section.  I think
it's the semantics of self.name: an ArgSection's name is the parameter
name, a non-ArgSection can either be a "meta" section (name is one of
"Returns"", "Since", "Note", "Notes", "Example", "Examples") or an
anonymous section (name is "").

> +
> +    def __init__(self, parser):
> +        self.parser = parser
> +        self.symbol = None
> +        self.body = QAPIDoc.Section()
> +        # a dict {'arg': ArgSection, ...}

Works.  Alternatevely:

           # dict mapping parameter name to ArgSection

> +        self.args = OrderedDict()
> +        # a list of Section
> +        self.meta = []
> +        # the current section
> +        self.section = self.body
> +        # associated expression and info (set by expression parser)

"will be set by expression parser", or "to be set by expression parser"?

> +        self.expr = None
> +        self.info = None

Clearer now.  The one remark I still have is on the use of "meta" isn't
obvious here.  I think a comment further up explaining the different
kinds of sections would help.

To be honest, I don't get what's "meta" about the "meta" sections :)

> +
> +    def has_meta(self, name):
> +        """Returns True if the doc has a meta section 'name'"""

PEP257[1] demands imperative mood, and punctuation:

           """Return True if the doc has a meta section 'name'."""

Unfortunately, there seems to be no established convention for marking
parameter names.  You put this one in single quotes, which works here,
but could be confusing in other cases.  I'd sidestep it:

           """Return True if we have a meta section with this name."""

or

           """Does a meta section with this name exist?"""

> +        for i in self.meta:
> +            if i.name == name:
> +                return True
> +        return False
> +
> +    def append(self, line):
> +        """Adds a # comment line, to be parsed and added to current section"""

Imperative mood:

    """Add a comment line, to be parsed and added to the current section."""

However, we're not always adding to the current section, we can also
start a new one.  The following avoids suggesting anything about the
current section:

    """Parse a comment line and add it to the documentation."""

When the function name is a verb, and its doc string starts with a
different verb, the name might be suboptimal.  Use your judgement.

If this one-liner is too terse, we can try a multi-line doc string:
    
    """Parse a comment line and add it to the documentation.

    TODO should we tell more about how we parse?

    Args:
        line: the comment starting with '#', with the newline stripped.

    Raises:
        QAPISchemaError: TODO explain error conditions
    """

Format stolen from the Google Python Style Guide[2], because PEP257 is
of not much help there.

Once you start with such elaborate doc strings, pressure will likely
mount to do the same elsewhere.  Could be a distraction right now.  Use
your judgement.

> +        line = line[1:]
> +        if not line:
> +            self._append_freeform(line)
> +            return
> +
> +        if line[0] != ' ':
> +            raise QAPISchemaError(self.parser, "missing space after #")
> +        line = line[1:]

QAPISchemaError takes the error position from its QAPISchemaParser
argument.  In other words, the error position depends on the state of
the parser when it calls this function.  Turns out the position is the
beginning of the comment.  Servicable here.  Action at a distance,
though.

Less strict:

           # strip leading # and space
           line = line[1:]
           if line[0] == ' ':
               line = line[1:]

Also avoids special-casing empty comments.

Perhaps we should treat all leading whitespace the same (I'm not sure):

           # strip leading # and whitespace
           line = line[1:]
           line = line.lstrip()

> +
> +        if self.symbol:
> +            self._append_symbol_line(line)
> +        elif (not self.body.content and
> +              line.startswith("@") and line.endswith(":")):
> +            self.symbol = line[1:-1]
> +            if not self.symbol:
> +                raise QAPISchemaError(self.parser, "Invalid symbol")

Doesn't recognize the symbol when there's anything other than a single
space between # and @.  Pathological case: '#\t@foo:' starting in column
6 looks exactly like '# @foo:', but doesn't work.  Fortunately, your
code rejects the tab there.  Assigning meaning to (leading) whitespace
may make sense, but it must be documented clearly.

Doesn't recognize the symbol when there's crap after ":".  Confusing
when it's invisible crap, i.e. trailing whitespace.  In general, it's
best to parse left to right, like this:

           elif not self.body.content and line.startswith('@'):
               match = valid_name.match(line[1:]);
               if not match:
                   raise QAPISchemaError(self.parser, 'Invalid name')
               if line.startswith(':', match.end(0)):
                   raise QAPISchemaError(self.parser, 'Expected ":")

Here, the error position is clearly suboptimal: it's the beginning of
the comment instead of where the actual error is, namely the beginning /
the end of the name.  More of the same below.  The root cause is
stacking parsers.  I'm not asking you to do anything about it at this
time.

Note my use of "name" rather than "symbol" in the error message.
qapi-code-gen.txt talks about names, not symbols.  Consistent use of
terminology matters.

> +        else:
> +            self._append_freeform(line)
> +
> +    def _append_symbol_line(self, line):
> +        name = line.split(' ', 1)[0]
> +
> +        if name.startswith("@") and name.endswith(":"):
> +            line = line[len(name)+1:]
> +            self._start_args_section(name[1:-1])

Similar issue.  Left to right:

           if re.match(r'\s*@', line):
               match = valid_name.match(line[1:]);
               if not match:
                   raise QAPISchemaError(self.parser, 'Invalid parameter name')
               line = line[match.end(0):]
               if line.startswith(':'):
                   raise QAPISchemaError(self.parser, 'Expected ":")
               line = line[1:]
               self._start_args_section(match.group(0))

Even better would be factoring out the common code to parse '@foo:'.

> +        elif name in ("Returns:", "Since:",
> +                      # those are often singular or plural
> +                      "Note:", "Notes:",
> +                      "Example:", "Examples:"):
> +            line = line[len(name)+1:]
> +            self._start_meta_section(name[:-1])

Since we're re.match()ing already, here's how do to it that way:

           else
               match = re.match(r'\s*(Returns|Since|Notes?|Examples?):', line)
               if match:
                   line = line[match.end(0):]
                   self._start_meta_section(match.group(1))

> +
> +        self._append_freeform(line)
> +
> +    def _start_args_section(self, name):
> +        if not name:
> +            raise QAPISchemaError(self.parser, "Invalid argument name")

parameter name

> +        if name in self.args:
> +            raise QAPISchemaError(self.parser, "'%s' arg duplicated" % name)

Duplicate parameter name

> +        self.section = QAPIDoc.ArgSection(name)
> +        self.args[name] = self.section
> +
> +    def _start_meta_section(self, name):
> +        if name in ("Returns", "Since") and self.has_meta(name):
> +            raise QAPISchemaError(self.parser,
> +                                  "Duplicated '%s' section" % name)
> +        self.section = QAPIDoc.Section(name)
> +        self.meta.append(self.section)
> +
> +    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] \
> +           and line and not line[0].isspace():

PEP8[3] prefers parenthesises over backslash:

           if (in_arg and self.section.content and not self.section.content[-1]
               and line and not line[0].isspace()):

> +            # an empty line followed by a non-indented
> +            # comment ends the argument section
> +            self.section = self.body
> +            self._append_freeform(line)
> +            return

Switching back to the self.body section like this reorders the
documentation text.  I still think this is a terrible idea.  A dumb
script is exceedingly unlikely to improve human-written doc comment text
by reordering.  In the rare case it does, the doc comment source should
be reordered.

Here's an example where the doc generator happily creates unintelligible
garbage if I format CpuDefinitionInfo's doc comment in a slightly off
way:

    ##
    # @CpuDefinitionInfo:
    #
    # Virtual CPU definition.
    #
    # @name: the name of the CPU definition
    # 
    # @migration-safe: #optional whether a CPU definition can be safely
    # used for migration in combination with a QEMU compatibility
    # machine when migrating between different QMU versions and between
    # hosts with different sets of (hardware or software)
    # capabilities.
    # 
    # If not provided, information is not available and callers should
    # not assume the CPU definition to be migration-safe. (since 2.8)
    #
    # @static: whether a CPU definition is static and will not change
    # depending on QEMU version, machine type, machine options and
    # accelerator options.  A static model is always
    # migration-safe. (since 2.8)
    #
    # @unavailable-features: #optional List of properties that prevent
    # the CPU model from running in the current host. (since 2.8)
    #
    # @unavailable-features is a list of QOM property names that
    # represent CPU model attributes that prevent the CPU from running.
    # If the QOM property is read-only, that means there's no known
    # way to make the CPU model run in the current host. Implementations
    # that choose not to provide specific information return the
    # property name "type".
    #
    # If the property is read-write, it means that it MAY be possible
    # to run the CPU model in the current host if that property is
    # changed. Management software can use it as hints to suggest or
    # choose an alternative for the user, or just to generate meaningful
    # error messages explaining why the CPU model can't be used.
    # If @unavailable-features is an empty list, the CPU model is
    # runnable using the current host and machine-type.
    # If @unavailable-features is not present, runnability
    # information for the CPU is not available.
    #
    # Since: 1.2.0
    ##
    { 'struct': 'CpuDefinitionInfo',
      'data': { 'name': 'str', '*migration-safe': 'bool', 'static': 'bool',
                '*unavailable-features': [ 'str' ] } }

To detect the problem, you have to read the generated docs attentively.
You know my opinion on our chances for that to happen during
development.

My point is not that we should support this doc comment format.  My
point is that people could conceivably write something like it, and not
get caught in patch review.

I can see three ways out of this swamp:

1. Let sections continue until another one begins.

2. Make text between sections an error.

3. When a section ends, start a new anonymous section.

Can't say offhand which one will work best.

> +        if in_arg or not self.section.name.startswith("Example"):
> +            line = line.strip()

Stripping whitespace is not "Markdown-like", because intendation carries
meaning in Markdown.  Example:

* First item in itemized list
* Second item
    * Sub-item of second item
    * Another sub-item
* Third item

Stripping whitespace destroys the list structure.  If that's what you
want, you get to document where your "Markdown-like" markup is unlike
Markdown :)

Is there a technical reason for stripping whitespace?

See also discussion of space after # above.

> +        self.section.append(line)
> +
> +
>  class QAPISchemaParser(object):
>  
>      def __init__(self, fp, previously_included=[], incl_info=None):
> @@ -137,11 +240,18 @@ class QAPISchemaParser(object):
>          self.line = 1
>          self.line_pos = 0
>          self.exprs = []
> +        self.docs = []
>          self.accept()
>  
>          while self.tok is not None:
>              info = {'file': fname, 'line': self.line,
>                      'parent': self.incl_info}
> +            if self.tok == '#' and self.val.startswith('##'):

How can self.val *not* start with '##' here?

> +                doc = self.get_doc()
> +                doc.info = info

Let's pass info as argument to get_doc(), so we don't have to dot into
doc here.  get_doc() can avoid dotting into doc by passing info to the
constructor.

> +                self.docs.append(doc)
> +                continue
> +
>              expr = self.get_expr(False)
>              if isinstance(expr, dict) and "include" in expr:
>                  if len(expr) != 1:
> @@ -160,6 +270,7 @@ class QAPISchemaParser(object):
>                          raise QAPILineError(info, "Inclusion loop for %s"
>                                              % include)
>                      inf = inf['parent']
> +
>                  # skip multiple include of the same file
>                  if incl_abs_fname in previously_included:
>                      continue
> @@ -171,12 +282,38 @@ class QAPISchemaParser(object):
>                  exprs_include = QAPISchemaParser(fobj, previously_included,
>                                                   info)
>                  self.exprs.extend(exprs_include.exprs)
> +                self.docs.extend(exprs_include.docs)
>              else:
>                  expr_elem = {'expr': expr,
>                               'info': info}
> +                if self.docs and not self.docs[-1].expr:
> +                    self.docs[-1].expr = expr
> +                    expr_elem['doc'] = self.docs[-1]
> +

Attaches the expression to the last doc comment that doesn't already
have one.  A bit sloppy, because there could be non-doc comments in
between, or the doc comment could be in another file.  It'll do, at
least for now.

>                  self.exprs.append(expr_elem)
>  
> -    def accept(self):
> +    def get_doc(self):
> +        if self.val != '##':
> +            raise QAPISchemaError(self, "Junk after '##' at start of "
> +                                  "documentation comment")
> +
> +        doc = QAPIDoc(self)
> +        self.accept(False)
> +        while self.tok == '#':
> +            if self.val.startswith('##'):
> +                # End of doc comment
> +                if self.val != '##':
> +                    raise QAPISchemaError(self, "Junk after '##' at end of "
> +                                          "documentation comment")
> +                self.accept()
> +                return doc
> +            else:
> +                doc.append(self.val)
> +            self.accept(False)
> +
> +        raise QAPISchemaError(self, "Documentation comment must end with '##'")

Let's put this after accept, next to the other get_FOO().

> +
> +    def accept(self, skip_comment=True):
>          while True:
>              self.tok = self.src[self.cursor]
>              self.pos = self.cursor
> @@ -184,7 +321,13 @@ class QAPISchemaParser(object):
>              self.val = None
>  
>              if self.tok == '#':
> +                if self.src[self.cursor] == '#':
> +                    # Start of doc comment
> +                    skip_comment = False
>                  self.cursor = self.src.find('\n', self.cursor)
> +                if not skip_comment:
> +                    self.val = self.src[self.pos:self.cursor]
> +                    return
>              elif self.tok in "{}:,[]":
>                  return
>              elif self.tok == "'":

Copied from review of v3, so I don't forget:

Comment tokens are thrown away as before, except when the parser asks
for them by passing skip_comment=False, or when the comment token starts
with ##.  The parser asks while parsing a doc comment, in get_doc().

This is a backchannel from the parser to the lexer.  I'd rather avoid
such lexer hacks, but I guess we can address that on top.

A comment starting with ## inside an expression is now a syntax error.
For instance, input

    {
    ##

yields

    /dev/stdin:2:1: Expected string or "}"

Rather unfriendly error message, but we can fix that on top.

> @@ -713,7 +856,7 @@ def check_keys(expr_elem, meta, required, optional=[]):
>                                  % (key, meta, name))
>  
>  
> -def check_exprs(exprs):
> +def check_exprs(exprs, strict_doc):

Note: strict_doc=False unless this is qapi2texi.py.

>      global all_names
>  
>      # Learn the types and check for valid expression keys
> @@ -722,6 +865,11 @@ def check_exprs(exprs):
>      for expr_elem in exprs:
>          expr = expr_elem['expr']
>          info = expr_elem['info']
> +
> +        if strict_doc and 'doc' not in expr_elem:
> +            raise QAPILineError(info,
> +                                "Expression missing documentation comment")
> +

Why should we supress this error in programs other than qapi2texi.py?

Can't see a test for this one.

>          if 'enum' in expr:
>              check_keys(expr_elem, 'enum', ['data'], ['prefix'])
>              add_enum(expr['enum'], info, expr['data'])
> @@ -780,6 +928,63 @@ def check_exprs(exprs):
>      return exprs
>  
>  
> +def check_simple_doc(doc):

You call this "free-form" elsewhere.  Pick one name and stick to it.
I think free-form is more descriptive than simple.

> +    if doc.symbol:
> +        raise QAPILineError(doc.info,
> +                            "'%s' documention is not followed by the definition"
> +                            % doc.symbol)

"Documentation for %s is not ..."

> +
> +    body = str(doc.body)
> +    if re.search(r'@\S+:', body, re.MULTILINE):
> +        raise QAPILineError(doc.info,
> +                            "Document body cannot contain @NAME: sections")
> +
> +
> +def check_expr_doc(doc, expr, info):

You call this "symbol" elsewhere.  I think "definition" would be better
than either.

> +    for i in ('enum', 'union', 'alternate', 'struct', 'command', 'event'):
> +        if i in expr:
> +            meta = i
> +            break
> +
> +    name = expr[meta]
> +    if doc.symbol != name:
> +        raise QAPILineError(info, "Definition of '%s' follows documentation"
> +                            " for '%s'" % (name, doc.symbol))
> +    if doc.has_meta('Returns') and 'command' not in expr:
> +        raise QAPILineError(info, "Invalid return documentation")

Suggest something like "'Returns:' is only valid for commands".

We accept 'Returns:' even when the command doesn't return anything,
because we currently use it to document errors, too.  Can't say I like
that, but it's out of scope here.

> +
> +    doc_args = set(doc.args.keys())
> +    if meta == 'union':
> +        data = expr.get('base', [])
> +    else:
> +        data = expr.get('data', [])
> +    if isinstance(data, dict):
> +        data = data.keys()
> +    args = set([name.strip('*') for name in data])

Not fixed since v3:

* Flat union where 'base' is a string, e.g. union UserDefFlatUnion in
  qapi-schema-test.json has base 'UserDefUnionBase', args is set(['a',
  'B', 'e', 'D', 'f', 'i', 'o', 'n', 's', 'r', 'U'])

* Command where 'data' is a string, e.g. user_def_cmd0 in
  qapi-schema-test.json has data 'Empty2', args is set(['E', 'm', 'p',
  '2', 't', 'y'])

* Event where 'data' is a string, no test case handy (hole in test
  coverage)

> +    if meta == 'alternate' or \
> +       (meta == 'union' and not expr.get('discriminator')):
> +        args.add('type')

As explained in review of v3, this is only a subset of the real set of
members.  Computing the exact set is impractical when working with the
abstract syntax tree.  I believe we'll eventually have to rewrite this
code to work with the QAPISchemaEntity instead.

> +    if not doc_args.issubset(args):
> +        raise QAPILineError(info, "Members documentation is not a subset of"
> +                            " API %r > %r" % (list(doc_args), list(args)))
> +
> +
> +def check_docs(docs):
> +    for doc in docs:
> +        for section in doc.args.values() + doc.meta:
> +            content = str(section)
> +            if not content or content.isspace():
> +                raise QAPILineError(doc.info,
> +                                    "Empty doc section '%s'" % section.name)
> +
> +        if not doc.expr:
> +            check_simple_doc(doc)
> +        else:
> +            check_expr_doc(doc, doc.expr, doc.info)
> +
> +    return docs
> +
> +
>  #
>  # Schema compiler frontend
>  #
> @@ -1249,9 +1454,11 @@ class QAPISchemaEvent(QAPISchemaEntity):
>  
>  
>  class QAPISchema(object):
> -    def __init__(self, fname):
> +    def __init__(self, fname, strict_doc=False):
>          try:
> -            self.exprs = check_exprs(QAPISchemaParser(open(fname, "r")).exprs)
> +            parser = QAPISchemaParser(open(fname, "r"))
> +            self.exprs = check_exprs(parser.exprs, strict_doc)
> +            self.docs = check_docs(parser.docs)
>              self._entity_dict = {}
>              self._predefining = True
>              self._def_predefineds()
> diff --git a/scripts/qapi2texi.py b/scripts/qapi2texi.py
> new file mode 100755
> index 0000000..0cec43a
> --- /dev/null
> +++ b/scripts/qapi2texi.py

Still only skimming this one.

> @@ -0,0 +1,331 @@
> +#!/usr/bin/env python
> +# QAPI texi generator
> +#
> +# This work is licensed under the terms of the GNU LGPL, version 2+.
> +# See the COPYING file in the top-level directory.
> +"""This script produces the documentation of a qapi schema in texinfo format"""
> +import re
> +import sys
> +
> +import qapi
> +
> +COMMAND_FMT = """
> +@deftypefn {type} {{{ret}}} {name} @
> +{{{args}}}
> +
> +{body}
> +
> +@end deftypefn
> +
> +""".format
> +
> +ENUM_FMT = """
> +@deftp Enum {name}
> +
> +{body}
> +
> +@end deftp
> +
> +""".format
> +
> +STRUCT_FMT = """
> +@deftp {{{type}}} {name} @
> +{{{attrs}}}
> +
> +{body}
> +
> +@end deftp
> +
> +""".format
> +
> +EXAMPLE_FMT = """@example
> +{code}
> +@end example
> +""".format
> +
> +
> +def subst_strong(doc):
> +    """Replaces *foo* by @strong{foo}"""
> +    return re.sub(r'\*([^_\n]+)\*', r'@emph{\1}', doc)
> +
> +
> +def subst_emph(doc):
> +    """Replaces _foo_ by @emph{foo}"""
> +    return re.sub(r'\s_([^_\n]+)_\s', r' @emph{\1} ', doc)
> +
> +
> +def subst_vars(doc):
> +    """Replaces @var by @code{var}"""
> +    return re.sub(r'@([\w-]+)', r'@code{\1}', doc)
> +
> +
> +def subst_braces(doc):
> +    """Replaces {} with @{ @}"""
> +    return doc.replace("{", "@{").replace("}", "@}")
> +
> +
> +def texi_example(doc):
> +    """Format @example"""
> +    doc = subst_braces(doc).strip('\n')
> +    return EXAMPLE_FMT(code=doc)
> +
> +
> +def texi_comment(doc):
> +    """
> +    Format a comment
> +
> +    Lines starting with:
> +    - |: generates an @example
> +    - =: generates @section
> +    - ==: generates @subsection
> +    - 1. or 1): generates an @enumerate @item
> +    - o/*/-: generates an @itemize list
> +    """
> +    lines = []
> +    doc = subst_braces(doc)
> +    doc = subst_vars(doc)
> +    doc = subst_emph(doc)
> +    doc = subst_strong(doc)
> +    inlist = ""
> +    lastempty = False
> +    for line in doc.split('\n'):
> +        empty = line == ""
> +
> +        if line.startswith("| "):
> +            line = EXAMPLE_FMT(code=line[2:])
> +        elif line.startswith("= "):
> +            line = "@section " + line[2:]
> +        elif line.startswith("== "):
> +            line = "@subsection " + line[3:]
> +        elif re.match("^([0-9]*[.)]) ", line):
> +            if not inlist:
> +                lines.append("@enumerate")
> +                inlist = "enumerate"
> +            line = line[line.find(" ")+1:]
> +            lines.append("@item")
> +        elif re.match("^[o*-] ", line):
> +            if not inlist:
> +                lines.append("@itemize %s" % {'o': "@bullet",
> +                                              '*': "@minus",
> +                                              '-': ""}[line[0]])
> +                inlist = "itemize"
> +            lines.append("@item")
> +            line = line[2:]
> +        elif lastempty and inlist:
> +            lines.append("@end %s\n" % inlist)
> +            inlist = ""
> +
> +        lastempty = empty
> +        lines.append(line)
> +
> +    if inlist:
> +        lines.append("@end %s\n" % inlist)
> +    return "\n".join(lines)
> +
> +
> +def texi_args(expr, key="data"):
> +    """
> +    Format the functions/structure/events.. arguments/members
> +    """
> +    if key not in expr:
> +        return ""
> +
> +    args = expr[key]
> +    arg_list = []
> +    if isinstance(args, str):
> +        arg_list.append(args)
> +    else:
> +        for name, typ in args.iteritems():
> +            # optional arg
> +            if name.startswith("*"):
> +                name = name[1:]
> +                arg_list.append("['%s': @var{%s}]" % (name, typ))
> +            # regular arg
> +            else:
> +                arg_list.append("'%s': @var{%s}" % (name, typ))

Inappropriate use of @var.  @var is for metasyntactic variables,
i.e. something that stands for another piece of text.  typ isn't, it's
the name of a specific QAPI type.  I think you should use @code.

This is the reason why the type names in qemu-qmp-ref.txt are often
mangled, e.g.

 -- Struct: VersionInfo { 'qemu': VERSIONTRIPLE, 'package': STR }

> +
> +    return ", ".join(arg_list)
> +
> +
> +def texi_body(doc):
> +    """
> +    Format the body of a symbol documentation:
> +    - a table of arguments
> +    - followed by "Returns/Notes/Since/Example" sections
> +    """
> +    def _section_order(section):
> +        return {"Returns": 0,
> +                "Note": 1,
> +                "Notes": 1,
> +                "Since": 2,
> +                "Example": 3,
> +                "Examples": 3}[section]
> +
> +    body = "@table @asis\n"
> +    for arg, section in doc.args.iteritems():
> +        desc = str(section)
> +        opt = ''
> +        if desc.startswith("#optional"):
> +            desc = desc[10:]
> +            opt = ' *'
> +        elif desc.endswith("#optional"):
> +            desc = desc[:-10]
> +            opt = ' *'
> +        body += "@item @code{'%s'}%s\n%s\n" % (arg, opt, texi_comment(desc))
> +    body += "@end table\n"
> +    body += texi_comment(str(doc.body))
> +
> +    meta = sorted(doc.meta, key=lambda i: _section_order(i.name))
> +    for section in meta:
> +        name, doc = (section.name, str(section))
> +        func = texi_comment
> +        if name.startswith("Example"):
> +            func = texi_example
> +
> +        body += "\n@quotation %s\n%s\n@end quotation" % \
> +                (name, func(doc))
> +    return body
> +
> +
> +def texi_alternate(expr, doc):
> +    """
> +    Format an alternate to texi
> +    """
> +    args = texi_args(expr)
> +    body = texi_body(doc)
> +    return STRUCT_FMT(type="Alternate",
> +                      name=doc.symbol,
> +                      attrs="[ " + args + " ]",
> +                      body=body)
> +
> +
> +def texi_union(expr, doc):
> +    """
> +    Format an union to texi

I think it's "a union".

> +    """
> +    attrs = "@{ " + texi_args(expr, "base") + " @}"
> +    discriminator = expr.get("discriminator")
> +    if not discriminator:
> +        union = "Flat Union"
> +        discriminator = "type"
> +    else:
> +        union = "Union"

Condition is backwards.

> +    attrs += " + '%s' = [ " % discriminator
> +    attrs += texi_args(expr, "data") + " ]"
> +    body = texi_body(doc)
> +
> +    return STRUCT_FMT(type=union,
> +                      name=doc.symbol,
> +                      attrs=attrs,
> +                      body=body)

You're inventing syntax here.  Example output:

 -- Union: QCryptoBlockOpenOptions { QCryptoBlockOptionsBase } +
          'format' = [ 'qcow': QCRYPTOBLOCKOPTIONSQCOW, 'luks':
          QCRYPTOBLOCKOPTIONSLUKS ]

     The options that are available for all encryption formats when
     opening an existing volume
          Since: 2.6

 -- Flat Union: ImageInfoSpecific { } + 'type' = [ 'qcow2':
          IMAGEINFOSPECIFICQCOW2, 'vmdk': IMAGEINFOSPECIFICVMDK, 'luks':
          QCRYPTOBLOCKINFOLUKS ]

     A discriminated record of image format specific information
     structures.
          Since: 1.7

Note that QCryptoBlockOpenOptions is actually a flat union, and
ImageInfoSpecific a simple union.  As I said, the condition is
backwards.

The meaning of the attrs part is unobvious.  Familiarity with schema
syntax doesn't really help.

Could we simply use schema syntax here?

If not: whatever format you use, you first need to explain it.

> +
> +
> +def texi_enum(expr, doc):
> +    """
> +    Format an enum to texi
> +    """
> +    for i in expr['data']:
> +        if i not in doc.args:
> +            doc.args[i] = ''
> +    body = texi_body(doc)
> +    return ENUM_FMT(name=doc.symbol,
> +                    body=body)
> +
> +
> +def texi_struct(expr, doc):
> +    """
> +    Format a struct to texi
> +    """
> +    args = texi_args(expr)
> +    body = texi_body(doc)
> +    attrs = "@{ " + args + " @}"
> +    base = expr.get("base")
> +    if base:
> +        attrs += " + %s" % base
> +    return STRUCT_FMT(type="Struct",
> +                      name=doc.symbol,
> +                      attrs=attrs,
> +                      body=body)

More syntax invention.  Example output:

 -- Struct: BlockdevOptionsReplication { 'mode': REPLICATIONMODE,
          ['top-id': STR] } + BlockdevOptionsGenericFormat

     ''mode''
          the replication mode
     ''top-id'' *
          In secondary mode, node name or device ID of the root node who
          owns the replication node chain.  Must not be given in primary
          mode.
     Driver specific block device options for replication
          Since: 2.8

Meaning of the attrs part is perhaps more guessable here, but it's still
guesswork.

The meaning of * after ''top-id'' is also unobvious.

Note the redundancy between the attrs part and the body: both state
member names and optionalness.  The body doesn't state member types and
base type.  If we fixed that, we could drop the attrs part and save us
the trouble of defining and explaining a syntax for it.

Let me take a step back.  This document is about the QMP wire format.
There are no such things as simple and flat unions on the wire, only
JSON objects.  QMP introspection duly describes a type's JSON objects,
not how it's defined in QAPI.  I think QMP documentation should ideally
do the same.

QMP introspection uses a common structure for struct, simple and flat
union: common members, variants, and if variants, then the common member
that serves as tag.  See introspect.json for details.

Base types are flattened away.  Appropriate for introspection, but
documentation shouldn't do that.

I wrote "ideally" because it's probably too big a step.  I'm willing to
settle for something less than ideal.

> +
> +
> +def texi_command(expr, doc):
> +    """
> +    Format a command to texi
> +    """
> +    args = texi_args(expr)
> +    ret = expr["returns"] if "returns" in expr else ""
> +    body = texi_body(doc)
> +    return COMMAND_FMT(type="Command",
> +                       name=doc.symbol,
> +                       ret=ret,
> +                       args="(" + args + ")",
> +                       body=body)
> +
> +
> +def texi_event(expr, doc):
> +    """
> +    Format an event to texi
> +    """
> +    args = texi_args(expr)
> +    body = texi_body(doc)
> +    return COMMAND_FMT(type="Event",
> +                       name=doc.symbol,
> +                       ret="",
> +                       args="(" + args + ")",
> +                       body=body)
> +
> +
> +def texi_expr(expr, doc):
> +    """
> +    Format an expr to texi
> +    """
> +    (kind, _) = expr.items()[0]
> +
> +    fmt = {"command": texi_command,
> +           "struct": texi_struct,
> +           "enum": texi_enum,
> +           "union": texi_union,
> +           "alternate": texi_alternate,
> +           "event": texi_event}
> +    try:
> +        fmt = fmt[kind]
> +    except KeyError:
> +        raise ValueError("Unknown expression kind '%s'" % kind)

The try / except converts one kind of error into another.  What does
that buy us?  As far as I can tell, this shouldn't ever happen.

> +
> +    return fmt(expr, doc)
> +
> +
> +def texi(docs):
> +    """
> +    Convert QAPI schema expressions to texi documentation
> +    """
> +    res = []
> +    for doc in docs:
> +        expr = doc.expr
> +        if not expr:
> +            res.append(texi_body(doc))
> +            continue
> +        try:
> +            doc = texi_expr(expr, doc)
> +            res.append(doc)
> +        except:
> +            print >>sys.stderr, "error at @%s" % doc.info
> +            raise
> +
> +    return '\n'.join(res)
> +
> +
> +def main(argv):
> +    """
> +    Takes schema argument, prints result to stdout
> +    """
> +    if len(argv) != 2:
> +        print >>sys.stderr, "%s: need exactly 1 argument: SCHEMA" % argv[0]
> +        sys.exit(1)
> +
> +    schema = qapi.QAPISchema(argv[1], strict_doc=True)
> +    print texi(schema.docs)
> +
> +
> +if __name__ == "__main__":
> +    main(sys.argv)
> diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
> index 2841c51..8bc963e 100644
> --- a/docs/qapi-code-gen.txt
> +++ b/docs/qapi-code-gen.txt
> @@ -45,16 +45,13 @@ QAPI parser does not).  At present, there is no place where a QAPI
>  schema requires the use of JSON numbers or null.
>  
>  Comments are allowed; anything between an unquoted # and the following
> -newline is ignored.  Although there is not yet a documentation
> -generator, a form of stylized comments has developed for consistently
> -documenting details about an expression and when it was added to the
> -schema.  The documentation is delimited between two lines of ##, then
> -the first line names the expression, an optional overview is provided,
> -then individual documentation about each member of 'data' is provided,
> -and finally, a 'Since: x.y.z' tag lists the release that introduced
> -the expression.  Optional members are tagged with the phrase
> -'#optional', often with their default value; and extensions added
> -after the expression was first released are also given a '(since
> +newline is ignored.  The documentation is delimited between two lines
> +of ##, then the first line names the expression, an optional overview
> +is provided, then individual documentation about each member of 'data'
> +is provided, and finally, a 'Since: x.y.z' tag lists the release that
> +introduced the expression.  Optional members are tagged with the
> +phrase '#optional', often with their default value; and extensions
> +added after the expression was first released are also given a '(since
>  x.y.z)' comment.  For example:
>  
>      ##
> @@ -73,12 +70,49 @@ x.y.z)' comment.  For example:
>      #           (Since 2.0)
>      #
>      # Since: 0.14.0
> +    #
> +    # Notes: You can also make a list:
> +    #        - with items
> +    #        - like this
> +    #
> +    # Example:
> +    #
> +    # -> { "execute": ... }
> +    # <- { "return": ... }
> +    #
>      ##
>      { 'struct': 'BlockStats',
>        'data': {'*device': 'str', 'stats': 'BlockDeviceStats',
>                 '*parent': 'BlockStats',
>                 '*backing': 'BlockStats'} }

This example gives an idea of how to document struct types.  But the
reader is left guessing how to document other kinds of definitions.  In
particular, there's no mention of "Returns", "Note" and "Examples"
sections anywhere in this file.  As is, this could do for a tutorial,
but this file is a *reference*, not a tutorial.

For a reference, we need to be more thorough.  A doc comments section on
its own seems advisable.

I guess doc comment examples are best added to the schema code examples
in sections === Struct types ===, ..., === Events ===.

Careful review for completeness is advised.

> +It's also possible to create documentation sections, such as:
> +
> +    ##
> +    # = Section
> +    # == Subsection
> +    #
> +    # Some text foo with *strong* and _emphasis_
> +    # 1. with a list
> +    # 2. like that
> +    #
> +    # And some code:
> +    # | $ echo foo
> +    # | -> do this
> +    # | <- get that
> +    #
> +    ##
> +
> +Text *foo* and _foo_ are for "strong" and "emphasis" styles (they do
> +not work over multiple lines). @foo is used to reference a symbol.
> +
> +Lines starting with the following characters and a space:
> +- | are examples
> +- = are top section
> +- == are subsection
> +- X. or X) are enumerations (X is any number)
> +- o/*/- are itemized list
> +

Is this doc markup documentation complete?

Is it related to any existing text markup language?  Hmm, the commit
message calls it "Markdown-like".  Should we mention that here?

Is all markup valid in all contexts?  For instance, is = Section valid
in symbol blocks?  What does | (or any markup for that matter) do within
an Example section?

>  The schema sets up a series of types, as well as commands and events
>  that will use those types.  Forward references are allowed: the parser
>  scans in two passes, where the first pass learns all type names, and
> diff --git a/tests/qapi-schema/doc-bad-args.err b/tests/qapi-schema/doc-bad-args.err
> new file mode 100644
> index 0000000..a55e003
> --- /dev/null
> +++ b/tests/qapi-schema/doc-bad-args.err
> @@ -0,0 +1 @@
> +tests/qapi-schema/doc-bad-args.json:1: Members documentation is not a subset of API ['a', 'b'] > ['a']
> diff --git a/tests/qapi-schema/doc-bad-args.exit b/tests/qapi-schema/doc-bad-args.exit
> new file mode 100644
> index 0000000..d00491f
> --- /dev/null
> +++ b/tests/qapi-schema/doc-bad-args.exit
> @@ -0,0 +1 @@
> +1
> diff --git a/tests/qapi-schema/doc-bad-args.json b/tests/qapi-schema/doc-bad-args.json
> new file mode 100644
> index 0000000..26992ea
> --- /dev/null
> +++ b/tests/qapi-schema/doc-bad-args.json
> @@ -0,0 +1,6 @@
> +##
> +# @foo:
> +# @a: a
> +# @b: b
> +##
> +{ 'command': 'foo', 'data': {'a': 'int'} }

The existing tests/qapi-schema/*.json explain what is being tested in a
comment.  Perhaps like this:

    # Arguments listed in the doc comment must exist in the actual schema

    ##
    # @foo:
    ...

Likewise for other tests.

> diff --git a/tests/qapi-schema/doc-bad-args.out b/tests/qapi-schema/doc-bad-args.out
> new file mode 100644
> index 0000000..e69de29
> diff --git a/tests/qapi-schema/doc-bad-symbol.err b/tests/qapi-schema/doc-bad-symbol.err
> new file mode 100644
> index 0000000..9c969d1
> --- /dev/null
> +++ b/tests/qapi-schema/doc-bad-symbol.err
> @@ -0,0 +1 @@
> +tests/qapi-schema/doc-bad-symbol.json:1: Definition of 'foo' follows documentation for 'food'
> diff --git a/tests/qapi-schema/doc-bad-symbol.exit b/tests/qapi-schema/doc-bad-symbol.exit
> new file mode 100644
> index 0000000..d00491f
> --- /dev/null
> +++ b/tests/qapi-schema/doc-bad-symbol.exit
> @@ -0,0 +1 @@
> +1
> diff --git a/tests/qapi-schema/doc-bad-symbol.json b/tests/qapi-schema/doc-bad-symbol.json
> new file mode 100644
> index 0000000..7255152
> --- /dev/null
> +++ b/tests/qapi-schema/doc-bad-symbol.json
> @@ -0,0 +1,4 @@
> +##
> +# @food:
> +##
> +{ 'command': 'foo', 'data': {'a': 'int'} }
> diff --git a/tests/qapi-schema/doc-bad-symbol.out b/tests/qapi-schema/doc-bad-symbol.out
> new file mode 100644
> index 0000000..e69de29
> diff --git a/tests/qapi-schema/doc-duplicated-arg.err b/tests/qapi-schema/doc-duplicated-arg.err
> new file mode 100644
> index 0000000..88a272b
> --- /dev/null
> +++ b/tests/qapi-schema/doc-duplicated-arg.err
> @@ -0,0 +1 @@
> +tests/qapi-schema/doc-duplicated-arg.json:4:1: 'a' arg duplicated
> diff --git a/tests/qapi-schema/doc-duplicated-arg.exit b/tests/qapi-schema/doc-duplicated-arg.exit
> new file mode 100644
> index 0000000..d00491f
> --- /dev/null
> +++ b/tests/qapi-schema/doc-duplicated-arg.exit
> @@ -0,0 +1 @@
> +1
> diff --git a/tests/qapi-schema/doc-duplicated-arg.json b/tests/qapi-schema/doc-duplicated-arg.json
> new file mode 100644
> index 0000000..f7804c2
> --- /dev/null
> +++ b/tests/qapi-schema/doc-duplicated-arg.json
> @@ -0,0 +1,5 @@
> +##
> +# @foo:
> +# @a:
> +# @a:
> +##
> diff --git a/tests/qapi-schema/doc-duplicated-arg.out b/tests/qapi-schema/doc-duplicated-arg.out
> new file mode 100644
> index 0000000..e69de29
> diff --git a/tests/qapi-schema/doc-duplicated-return.err b/tests/qapi-schema/doc-duplicated-return.err
> new file mode 100644
> index 0000000..1b02880
> --- /dev/null
> +++ b/tests/qapi-schema/doc-duplicated-return.err
> @@ -0,0 +1 @@
> +tests/qapi-schema/doc-duplicated-return.json:5:1: Duplicated 'Returns' section
> diff --git a/tests/qapi-schema/doc-duplicated-return.exit b/tests/qapi-schema/doc-duplicated-return.exit
> new file mode 100644
> index 0000000..d00491f
> --- /dev/null
> +++ b/tests/qapi-schema/doc-duplicated-return.exit
> @@ -0,0 +1 @@
> +1
> diff --git a/tests/qapi-schema/doc-duplicated-return.json b/tests/qapi-schema/doc-duplicated-return.json
> new file mode 100644
> index 0000000..de7234b
> --- /dev/null
> +++ b/tests/qapi-schema/doc-duplicated-return.json
> @@ -0,0 +1,6 @@
> +##
> +# @foo:
> +#
> +# Returns: 0
> +# Returns: 1
> +##
> diff --git a/tests/qapi-schema/doc-duplicated-return.out b/tests/qapi-schema/doc-duplicated-return.out
> new file mode 100644
> index 0000000..e69de29
> diff --git a/tests/qapi-schema/doc-duplicated-since.err b/tests/qapi-schema/doc-duplicated-since.err
> new file mode 100644
> index 0000000..72efb2a
> --- /dev/null
> +++ b/tests/qapi-schema/doc-duplicated-since.err
> @@ -0,0 +1 @@
> +tests/qapi-schema/doc-duplicated-since.json:5:1: Duplicated 'Since' section
> diff --git a/tests/qapi-schema/doc-duplicated-since.exit b/tests/qapi-schema/doc-duplicated-since.exit
> new file mode 100644
> index 0000000..d00491f
> --- /dev/null
> +++ b/tests/qapi-schema/doc-duplicated-since.exit
> @@ -0,0 +1 @@
> +1
> diff --git a/tests/qapi-schema/doc-duplicated-since.json b/tests/qapi-schema/doc-duplicated-since.json
> new file mode 100644
> index 0000000..da261a1
> --- /dev/null
> +++ b/tests/qapi-schema/doc-duplicated-since.json
> @@ -0,0 +1,6 @@
> +##
> +# @foo:
> +#
> +# Since: 0
> +# Since: 1
> +##
> diff --git a/tests/qapi-schema/doc-duplicated-since.out b/tests/qapi-schema/doc-duplicated-since.out
> new file mode 100644
> index 0000000..e69de29
> diff --git a/tests/qapi-schema/doc-empty-arg.err b/tests/qapi-schema/doc-empty-arg.err
> new file mode 100644
> index 0000000..0647eed
> --- /dev/null
> +++ b/tests/qapi-schema/doc-empty-arg.err
> @@ -0,0 +1 @@
> +tests/qapi-schema/doc-empty-arg.json:3:1: Invalid argument name
> diff --git a/tests/qapi-schema/doc-empty-arg.exit b/tests/qapi-schema/doc-empty-arg.exit
> new file mode 100644
> index 0000000..d00491f
> --- /dev/null
> +++ b/tests/qapi-schema/doc-empty-arg.exit
> @@ -0,0 +1 @@
> +1
> diff --git a/tests/qapi-schema/doc-empty-arg.json b/tests/qapi-schema/doc-empty-arg.json
> new file mode 100644
> index 0000000..74f526c
> --- /dev/null
> +++ b/tests/qapi-schema/doc-empty-arg.json
> @@ -0,0 +1,4 @@
> +##
> +# @foo:
> +# @:
> +##
> diff --git a/tests/qapi-schema/doc-empty-arg.out b/tests/qapi-schema/doc-empty-arg.out
> new file mode 100644
> index 0000000..e69de29
> diff --git a/tests/qapi-schema/doc-empty-section.err b/tests/qapi-schema/doc-empty-section.err
> new file mode 100644
> index 0000000..c940332
> --- /dev/null
> +++ b/tests/qapi-schema/doc-empty-section.err
> @@ -0,0 +1 @@
> +tests/qapi-schema/doc-empty-section.json:1: Empty doc section 'Note'
> diff --git a/tests/qapi-schema/doc-empty-section.exit b/tests/qapi-schema/doc-empty-section.exit
> new file mode 100644
> index 0000000..d00491f
> --- /dev/null
> +++ b/tests/qapi-schema/doc-empty-section.exit
> @@ -0,0 +1 @@
> +1
> diff --git a/tests/qapi-schema/doc-empty-section.json b/tests/qapi-schema/doc-empty-section.json
> new file mode 100644
> index 0000000..ed3867d
> --- /dev/null
> +++ b/tests/qapi-schema/doc-empty-section.json
> @@ -0,0 +1,6 @@
> +##
> +# @foo:
> +#
> +# Note:
> +##
> +{ 'command': 'foo', 'data': {'a': 'int'} }
> diff --git a/tests/qapi-schema/doc-empty-section.out b/tests/qapi-schema/doc-empty-section.out
> new file mode 100644
> index 0000000..e69de29
> diff --git a/tests/qapi-schema/doc-empty-symbol.err b/tests/qapi-schema/doc-empty-symbol.err
> new file mode 100644
> index 0000000..955dc3c
> --- /dev/null
> +++ b/tests/qapi-schema/doc-empty-symbol.err
> @@ -0,0 +1 @@
> +tests/qapi-schema/doc-empty-symbol.json:2:1: Invalid symbol
> diff --git a/tests/qapi-schema/doc-empty-symbol.exit b/tests/qapi-schema/doc-empty-symbol.exit
> new file mode 100644
> index 0000000..d00491f
> --- /dev/null
> +++ b/tests/qapi-schema/doc-empty-symbol.exit
> @@ -0,0 +1 @@
> +1
> diff --git a/tests/qapi-schema/doc-empty-symbol.json b/tests/qapi-schema/doc-empty-symbol.json
> new file mode 100644
> index 0000000..8a2d662
> --- /dev/null
> +++ b/tests/qapi-schema/doc-empty-symbol.json
> @@ -0,0 +1,3 @@
> +##
> +# @:
> +##
> diff --git a/tests/qapi-schema/doc-empty-symbol.out b/tests/qapi-schema/doc-empty-symbol.out
> new file mode 100644
> index 0000000..e69de29
> diff --git a/tests/qapi-schema/doc-invalid-end.err b/tests/qapi-schema/doc-invalid-end.err
> new file mode 100644
> index 0000000..5ed8911
> --- /dev/null
> +++ b/tests/qapi-schema/doc-invalid-end.err
> @@ -0,0 +1 @@
> +tests/qapi-schema/doc-invalid-end.json:3:2: Documentation comment must end with '##'
> diff --git a/tests/qapi-schema/doc-invalid-end.exit b/tests/qapi-schema/doc-invalid-end.exit
> new file mode 100644
> index 0000000..d00491f
> --- /dev/null
> +++ b/tests/qapi-schema/doc-invalid-end.exit
> @@ -0,0 +1 @@
> +1
> diff --git a/tests/qapi-schema/doc-invalid-end.json b/tests/qapi-schema/doc-invalid-end.json
> new file mode 100644
> index 0000000..7452718
> --- /dev/null
> +++ b/tests/qapi-schema/doc-invalid-end.json
> @@ -0,0 +1,3 @@
> +##
> +# An invalid comment
> +#
> diff --git a/tests/qapi-schema/doc-invalid-end.out b/tests/qapi-schema/doc-invalid-end.out
> new file mode 100644
> index 0000000..e69de29
> diff --git a/tests/qapi-schema/doc-invalid-end2.err b/tests/qapi-schema/doc-invalid-end2.err
> new file mode 100644
> index 0000000..acd23bb
> --- /dev/null
> +++ b/tests/qapi-schema/doc-invalid-end2.err
> @@ -0,0 +1 @@
> +tests/qapi-schema/doc-invalid-end2.json:3:1: Junk after '##' at end of documentation comment
> diff --git a/tests/qapi-schema/doc-invalid-end2.exit b/tests/qapi-schema/doc-invalid-end2.exit
> new file mode 100644
> index 0000000..d00491f
> --- /dev/null
> +++ b/tests/qapi-schema/doc-invalid-end2.exit
> @@ -0,0 +1 @@
> +1
> diff --git a/tests/qapi-schema/doc-invalid-end2.json b/tests/qapi-schema/doc-invalid-end2.json
> new file mode 100644
> index 0000000..12e669e
> --- /dev/null
> +++ b/tests/qapi-schema/doc-invalid-end2.json
> @@ -0,0 +1,3 @@
> +##
> +#
> +## invalid
> diff --git a/tests/qapi-schema/doc-invalid-end2.out b/tests/qapi-schema/doc-invalid-end2.out
> new file mode 100644
> index 0000000..e69de29
> diff --git a/tests/qapi-schema/doc-invalid-return.err b/tests/qapi-schema/doc-invalid-return.err
> new file mode 100644
> index 0000000..c3f2691
> --- /dev/null
> +++ b/tests/qapi-schema/doc-invalid-return.err
> @@ -0,0 +1 @@
> +tests/qapi-schema/doc-invalid-return.json:1: Invalid return documentation
> diff --git a/tests/qapi-schema/doc-invalid-return.exit b/tests/qapi-schema/doc-invalid-return.exit
> new file mode 100644
> index 0000000..d00491f
> --- /dev/null
> +++ b/tests/qapi-schema/doc-invalid-return.exit
> @@ -0,0 +1 @@
> +1
> diff --git a/tests/qapi-schema/doc-invalid-return.json b/tests/qapi-schema/doc-invalid-return.json
> new file mode 100644
> index 0000000..6568b6d
> --- /dev/null
> +++ b/tests/qapi-schema/doc-invalid-return.json
> @@ -0,0 +1,5 @@
> +##
> +# @foo:
> +# Returns: blah
> +##
> +{ 'event': 'foo' }
> diff --git a/tests/qapi-schema/doc-invalid-return.out b/tests/qapi-schema/doc-invalid-return.out
> new file mode 100644
> index 0000000..e69de29
> diff --git a/tests/qapi-schema/doc-invalid-section.err b/tests/qapi-schema/doc-invalid-section.err
> new file mode 100644
> index 0000000..f4c12aa
> --- /dev/null
> +++ b/tests/qapi-schema/doc-invalid-section.err
> @@ -0,0 +1 @@
> +tests/qapi-schema/doc-invalid-section.json:1: Document body cannot contain @NAME: sections
> diff --git a/tests/qapi-schema/doc-invalid-section.exit b/tests/qapi-schema/doc-invalid-section.exit
> new file mode 100644
> index 0000000..d00491f
> --- /dev/null
> +++ b/tests/qapi-schema/doc-invalid-section.exit
> @@ -0,0 +1 @@
> +1
> diff --git a/tests/qapi-schema/doc-invalid-section.json b/tests/qapi-schema/doc-invalid-section.json
> new file mode 100644
> index 0000000..9afa2f1
> --- /dev/null
> +++ b/tests/qapi-schema/doc-invalid-section.json
> @@ -0,0 +1,4 @@
> +##
> +# freeform
> +# @note: foo
> +##
> diff --git a/tests/qapi-schema/doc-invalid-section.out b/tests/qapi-schema/doc-invalid-section.out
> new file mode 100644
> index 0000000..e69de29
> diff --git a/tests/qapi-schema/doc-invalid-start.err b/tests/qapi-schema/doc-invalid-start.err
> new file mode 100644
> index 0000000..194c8d7
> --- /dev/null
> +++ b/tests/qapi-schema/doc-invalid-start.err
> @@ -0,0 +1 @@
> +tests/qapi-schema/doc-invalid-start.json:1:1: Junk after '##' at start of documentation comment
> diff --git a/tests/qapi-schema/doc-invalid-start.exit b/tests/qapi-schema/doc-invalid-start.exit
> new file mode 100644
> index 0000000..d00491f
> --- /dev/null
> +++ b/tests/qapi-schema/doc-invalid-start.exit
> @@ -0,0 +1 @@
> +1
> diff --git a/tests/qapi-schema/doc-invalid-start.json b/tests/qapi-schema/doc-invalid-start.json
> new file mode 100644
> index 0000000..f85adfd
> --- /dev/null
> +++ b/tests/qapi-schema/doc-invalid-start.json
> @@ -0,0 +1,3 @@
> +## invalid
> +#
> +##
> diff --git a/tests/qapi-schema/doc-invalid-start.out b/tests/qapi-schema/doc-invalid-start.out
> new file mode 100644
> index 0000000..e69de29
> diff --git a/tests/qapi-schema/doc-missing-expr.err b/tests/qapi-schema/doc-missing-expr.err
> new file mode 100644
> index 0000000..e4ed135
> --- /dev/null
> +++ b/tests/qapi-schema/doc-missing-expr.err
> @@ -0,0 +1 @@
> +tests/qapi-schema/doc-missing-expr.json:1: 'bar' documention is not followed by the definition
> diff --git a/tests/qapi-schema/doc-missing-expr.exit b/tests/qapi-schema/doc-missing-expr.exit
> new file mode 100644
> index 0000000..d00491f
> --- /dev/null
> +++ b/tests/qapi-schema/doc-missing-expr.exit
> @@ -0,0 +1 @@
> +1
> diff --git a/tests/qapi-schema/doc-missing-expr.json b/tests/qapi-schema/doc-missing-expr.json
> new file mode 100644
> index 0000000..a0be2e1
> --- /dev/null
> +++ b/tests/qapi-schema/doc-missing-expr.json
> @@ -0,0 +1,3 @@
> +##
> +# @bar:
> +##
> diff --git a/tests/qapi-schema/doc-missing-expr.out b/tests/qapi-schema/doc-missing-expr.out
> new file mode 100644
> index 0000000..e69de29
> diff --git a/tests/qapi-schema/doc-missing-space.err b/tests/qapi-schema/doc-missing-space.err
> new file mode 100644
> index 0000000..37056ce
> --- /dev/null
> +++ b/tests/qapi-schema/doc-missing-space.err
> @@ -0,0 +1 @@
> +tests/qapi-schema/doc-missing-space.json:3:1: missing space after #
> diff --git a/tests/qapi-schema/doc-missing-space.exit b/tests/qapi-schema/doc-missing-space.exit
> new file mode 100644
> index 0000000..d00491f
> --- /dev/null
> +++ b/tests/qapi-schema/doc-missing-space.exit
> @@ -0,0 +1 @@
> +1
> diff --git a/tests/qapi-schema/doc-missing-space.json b/tests/qapi-schema/doc-missing-space.json
> new file mode 100644
> index 0000000..39303de
> --- /dev/null
> +++ b/tests/qapi-schema/doc-missing-space.json
> @@ -0,0 +1,4 @@
> +##
> +# missing space:
> +#wef
> +##
> diff --git a/tests/qapi-schema/doc-missing-space.out b/tests/qapi-schema/doc-missing-space.out
> new file mode 100644
> index 0000000..e69de29
> diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
> index 1719463..d921ec4 100644
> --- a/tests/qapi-schema/qapi-schema-test.json
> +++ b/tests/qapi-schema/qapi-schema-test.json
> @@ -3,6 +3,43 @@
>  # This file is a stress test of supported qapi constructs that must
>  # parse and compile correctly.
>  
> +##
> +# = Section
> +# == subsection
> +#
> +# Some text foo with *strong* and _emphasis_
> +# 1. with a list
> +# 2. like that @foo
> +#
> +# And some code:
> +# | $ echo foo
> +# | -> do this
> +# | <- get that
> +#
> +# Note: is not a meta
> +##
> +
> +##
> +# @TestStruct:
> +# @integer: foo
> +#           blah
> +#
> +#           bao
> +#
> +# @boolean: bar
> +# @string: baz
> +#
> +# body with @var
> +#
> +# Example:
> +#
> +# -> { "execute": ... }
> +# <- { "return": ... }
> +#
> +# Since: 2.3
> +# Note: a note
> +#
> +##
>  { 'struct': 'TestStruct',
>    'data': { 'integer': 'int', 'boolean': 'bool', 'string': 'str' } }
>  
> @@ -123,6 +160,27 @@
>    'data': {'ud1a': 'UserDefOne', '*ud1b': 'UserDefOne'},
>    'returns': 'UserDefTwo' }
>  
> +##
> +# Another comment
> +##
> +
> +##
> +# @guest-get-time:
> +#
> +# @a: an integer
> +# @b: #optional integer
> +#
> +# @guest-get-time body
> +#
> +# Returns: returns something
> +#
> +# Example:
> +#
> +# -> { "execute": "guest-get-time", ... }
> +# <- { "return": "42" }
> +#
> +##
> +
>  # Returning a non-dictionary requires a name from the whitelist
>  { 'command': 'guest-get-time', 'data': {'a': 'int', '*b': 'int' },
>    'returns': 'int' }
> diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
> index 9d99c4e..fde9e06 100644
> --- a/tests/qapi-schema/qapi-schema-test.out
> +++ b/tests/qapi-schema/qapi-schema-test.out
> @@ -232,3 +232,52 @@ command user_def_cmd1 q_obj_user_def_cmd1-arg -> None
>     gen=True success_response=True boxed=False
>  command user_def_cmd2 q_obj_user_def_cmd2-arg -> UserDefTwo
>     gen=True success_response=True boxed=False
> +doc freeform
> +    body=
> += Section
> +== subsection
> +
> +Some text foo with *strong* and _emphasis_
> +1. with a list
> +2. like that @foo
> +
> +And some code:
> +| $ echo foo
> +| -> do this
> +| <- get that
> +
> +Note: is not a meta
> +doc symbol=TestStruct expr=('struct', 'TestStruct')
> +    arg=integer
> +foo
> +blah
> +
> +bao
> +    arg=boolean
> +bar
> +    arg=string
> +baz
> +    meta=Example
> +-> { "execute": ... }
> +<- { "return": ... }
> +    meta=Since
> +2.3
> +    meta=Note
> +a note
> +    body=
> +body with @var
> +doc freeform
> +    body=
> +Another comment
> +doc symbol=guest-get-time expr=('command', 'guest-get-time')
> +    arg=a
> +an integer
> +    arg=b
> +#optional integer
> +    meta=Returns
> +returns something
> +    meta=Example
> +-> { "execute": "guest-get-time", ... }
> +<- { "return": "42" }
> +    body=
> +@guest-get-time body
> diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
> index ef74e2c..22da014 100644
> --- a/tests/qapi-schema/test-qapi.py
> +++ b/tests/qapi-schema/test-qapi.py
> @@ -55,3 +55,15 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
>  
>  schema = QAPISchema(sys.argv[1])
>  schema.visit(QAPISchemaTestVisitor())
> +
> +for doc in schema.docs:
> +    if doc.symbol:
> +        print 'doc symbol=%s expr=%s' % \
> +            (doc.symbol, doc.expr.items()[0])
> +    else:
> +        print 'doc freeform'
> +    for arg, section in doc.args.iteritems():
> +        print '    arg=%s\n%s' % (arg, section)
> +    for section in doc.meta:
> +        print '    meta=%s\n%s' % (section.name, section)
> +    print '    body=\n%s' % doc.body


[1] https://www.python.org/dev/peps/pep-0257/
[2] https://google.github.io/styleguide/pyguide.html
[3] https://www.python.org/dev/peps/pep-0008/

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v5 09/17] qapi: add some sections in docs
  2016-11-30 15:38   ` Markus Armbruster
@ 2016-11-30 16:07     ` Marc-André Lureau
  0 siblings, 0 replies; 48+ messages in thread
From: Marc-André Lureau @ 2016-11-30 16:07 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: Marc-André Lureau, qemu-devel

Hi

----- Original Message -----
> Second thoughts...
> 
> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
> 
> > Add some more section titles to organize the produced documents.
> >
> > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> > ---
> >  qapi-schema.json     |  4 ++++
> >  qapi/block-core.json |  6 ++++--
> >  qapi/block.json      | 10 ++++++++--
> >  qapi/common.json     |  6 ++++--
> >  qapi/crypto.json     |  5 ++++-
> >  qapi/event.json      |  6 ++++++
> >  qapi/rocker.json     |  4 ++++
> >  qapi/trace.json      |  3 +++
> >  8 files changed, 37 insertions(+), 7 deletions(-)
> >
> > diff --git a/qapi-schema.json b/qapi-schema.json
> > index dfe68ba..69340f2 100644
> > --- a/qapi-schema.json
> > +++ b/qapi-schema.json
> > @@ -20,6 +20,10 @@
> >  # QAPI introspection
> >  { 'include': 'qapi/introspect.json' }
> >  
> > +##
> > +# = QMP commands
> > +##
> > +
> >  ##
> >  # @qmp_capabilities:
> >  #
> > diff --git a/qapi/block-core.json b/qapi/block-core.json
> > index ec1da2a..05cedc3 100644
> > --- a/qapi/block-core.json
> > +++ b/qapi/block-core.json
> > @@ -1,6 +1,8 @@
> >  # -*- Mode: Python -*-
> > -#
> > -# QAPI block core definitions (vm unrelated)
> > +
> > +##
> > +# == QAPI block core definitions (vm unrelated)
> > +##
> >  
> >  # QAPI common definitions
> >  { 'include': 'common.json' }
> > diff --git a/qapi/block.json b/qapi/block.json
> > index 937df05..e4ad74b 100644
> > --- a/qapi/block.json
> > +++ b/qapi/block.json
> > @@ -1,10 +1,16 @@
> >  # -*- Mode: Python -*-
> > -#
> > -# QAPI block definitions (vm related)
> > +
> > +##
> > +# = QAPI block definitions
> > +##
> >  
> >  # QAPI block core definitions
> >  { 'include': 'block-core.json' }
> >  
> > +##
> > +# == QAPI block definitions (vm unrelated)
> > +##
> > +
> >  ##
> >  # @BiosAtaTranslation:
> >  #
> > diff --git a/qapi/common.json b/qapi/common.json
> > index 624a861..d93f159 100644
> > --- a/qapi/common.json
> > +++ b/qapi/common.json
> > @@ -1,6 +1,8 @@
> >  # -*- Mode: Python -*-
> > -#
> > -# QAPI common definitions
> > +
> > +##
> > +# = QAPI common definitions
> > +##
> >  
> >  ##
> >  # @QapiErrorClass:
> > diff --git a/qapi/crypto.json b/qapi/crypto.json
> > index 15d296e..1e517b0 100644
> > --- a/qapi/crypto.json
> > +++ b/qapi/crypto.json
> > @@ -1,6 +1,9 @@
> >  # -*- Mode: Python -*-
> >  #
> > -# QAPI crypto definitions
> > +
> > +##
> > +# = QAPI crypto definitions
> > +##
> >  
> >  ##
> >  # @QCryptoTLSCredsEndpoint:
> > diff --git a/qapi/event.json b/qapi/event.json
> > index 37bf34e..59942b0 100644
> > --- a/qapi/event.json
> > +++ b/qapi/event.json
> > @@ -1,3 +1,9 @@
> > +# -*- Mode: Python -*-
> > +
> > +##
> > +# = Events
> > +##
> > +
> 
> This suggests *all* events are in this section, which isn't the case.
> "Other events"?  "Core events"?  Dunno...

"Other events" sounds nice to me. I think we could improve the organization after. 

> >  ##
> >  # @SHUTDOWN:
> >  #
> > diff --git a/qapi/rocker.json b/qapi/rocker.json
> > index ace2776..dd72e02 100644
> > --- a/qapi/rocker.json
> > +++ b/qapi/rocker.json
> > @@ -1,4 +1,8 @@
> >  ##
> > +# = Rocker API
> 
> What about "Rocker switch device"?

ok

> 
> > +##
> > +
> > +##
> >  # @RockerSwitch:
> >  #
> >  # Rocker switch information.
> > diff --git a/qapi/trace.json b/qapi/trace.json
> > index 4fd39b7..3ad7df7 100644
> > --- a/qapi/trace.json
> > +++ b/qapi/trace.json
> > @@ -5,6 +5,9 @@
> >  # This work is licensed under the terms of the GNU GPL, version 2 or
> >  later.
> >  # See the COPYING file in the top-level directory.
> >  
> > +##
> > +# = Tracing commands
> > +##
> >  
> >  ##
> >  # @TraceEventState:
> 

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v5 00/17] qapi doc generation (whole version, squashed)
  2016-11-17 15:54 [Qemu-devel] [PATCH v5 00/17] qapi doc generation (whole version, squashed) Marc-André Lureau
                   ` (16 preceding siblings ...)
  2016-11-17 15:55 ` [Qemu-devel] [PATCH v5 17/17] build-sys: add qapi doc generation targets Marc-André Lureau
@ 2016-12-05 16:53 ` Markus Armbruster
  17 siblings, 0 replies; 48+ messages in thread
From: Markus Armbruster @ 2016-12-05 16:53 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

Marc-André Lureau <marcandre.lureau@redhat.com> writes:

> Add a qapi2texi script to generate the documentation from the qapi
> schemas.
>
> The 15th patch in this series is a squashed version of the
> documentation move from qmp-commands.txt to the schemas. The whole
> version (not sent on the ML to avoid spamming) is in the following git
> branch: https://github.com/elmarco/qemu/commits/qapi-doc
>
> PDF preview:
> https://fedorapeople.org/~elmarco/qemu-qmp-ref.pdf

PATCH 2,4-8 applied to qapi-next for 2.8.

PATCH 1,10 are ready, but I'm punting them to 2.9 because they affect
generated code.  Probably overcautious.

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v5 13/17] qapi: add qapi2texi script
  2016-11-30 16:06   ` Markus Armbruster
@ 2016-12-05 17:35     ` Marc-André Lureau
  2016-12-06 11:50       ` Markus Armbruster
  0 siblings, 1 reply; 48+ messages in thread
From: Marc-André Lureau @ 2016-12-05 17:35 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel

Hi

On Wed, Nov 30, 2016 at 8:07 PM Markus Armbruster <armbru@redhat.com> wrote:

Marc-André Lureau <marcandre.lureau@redhat.com> writes:

> As the name suggests, the qapi2texi script converts JSON QAPI
> description into a texi file suitable for different target
> formats (info/man/txt/pdf/html...).
>
> It parses the following kind of blocks:
>
> Free-form:
>
>   ##
>   # = Section
>   # == Subsection
>   #
>   # Some text foo with *emphasis*
>   # 1. with a list
>   # 2. like that
>   #
>   # And some code:
>   # | $ echo foo
>   # | -> do this
>   # | <- get that
>   #
>   ##
>
> Symbol:
>
>   ##
>   # @symbol:
>   #
>   # Symbol body ditto ergo sum. Foo bar
>   # baz ding.
>   #
>   # @arg: foo
>   # @arg: #optional foo

Let's not use @arg twice.

Terminology: I prefer to use "parameter" for formal parameters, and
"argument" for actual arguments.  This matches how The Python Language
Reference uses the terms.

What about

    # @param1: the frob to frobnicate
    # @param2: #optional how hard to frobnicate


ok


>   #
>   # Returns: returns bla bla
>   #          Or bla blah

Repeating "returns" is awkward, and we don't do that in our schemas.

We need a period before "Or", or spell it "or".

What about

    # Returns: the frobnicated frob.
    #          If frob isn't frobnicatable, GenericError.


ok


>   #
>   # Since: version
>   # Notes: notes, comments can have
>   #        - itemized list
>   #        - like this
>   #
>   # Example:
>   #
>   # -> { "execute": "quit" }
>   # <- { "return": {} }
>   #
>   ##
>
> That's roughly following the following EBNF grammar:
>
> api_comment = "##\n" comment "##\n"
> comment = freeform_comment | symbol_comment
> freeform_comment = { "# " text "\n" | "#\n" }
> symbol_comment = "# @" name ":\n" { member | meta | freeform_comment }

Rejects non-empty comments where "#" is not followed by space.  Such
usage is accepted outside doc comments.  Hmm.

> member = "# @" name ':' [ text ] freeform_comment

Are you missing a "\n" before freeform_comment?


yes

> meta = "# " ( "Returns:", "Since:", "Note:", "Notes:", "Example:",
"Examples:" ) [ text ] freeform_comment

Likewise.


ok


> text = free-text markdown-like, "#optional" for members

The grammar is ambiguous: a line "# @foo:\n" can be parsed both as
freeform_comment and as symbol_comment.  Since your intent is obvious
enough, it can still serve as documentation.  It's not a suitable
foundation for parsing, though.  Okay for a commit message.

> Thanks to the following json expressions, the documentation is enhanced
> with extra information about the type of arguments and return value
> expected.

I guess you want to say that we enrich the documentation we extract from
comments with information from the actual schema.  Correct?


yes


Missing: a brief discussion of deficiencies.  These include:

* The generated QMP documentation includes internal types

  We use qapi-schema.json both for defining the external QMP interface
  and for defining internal types.  qmp-introspect.py carefully
  separates the two, to not expose internal types.  qapi2texi.py happily
  exposes everything.

  Currently, about a fifth of the types in the generated docs are
  internal:

      AcpiTableOptions
      BiosAtaTranslation
      BlockDeviceMapEntry
      COLOMessage
      COLOMode
      DummyForceArrays
      FailoverStatus
      FloppyDriveType
      ImageCheck
      LostTickPolicy
      MapEntry
      MigrationParameter
      NetClientDriver
      NetFilterDirection
      NetLegacy
      NetLegacyNicOptions
      NetLegacyOptions
      NetLegacyOptionsKind
      Netdev
      NetdevBridgeOptions
      NetdevDumpOptions
      NetdevHubPortOptions
      NetdevL2TPv3Options
      NetdevNetmapOptions
      NetdevNoneOptions
      NetdevSocketOptions
      NetdevTapOptions
      NetdevUserOptions
      NetdevVdeOptions
      NetdevVhostUserOptions
      NumaNodeOptions
      NumaOptions
      NumaOptionsKind
      OnOffAuto
      OnOffSplit
      PreallocMode
      QCryptoBlockCreateOptions
      QCryptoBlockCreateOptionsLUKS
      QCryptoBlockFormat
      QCryptoBlockInfo
      QCryptoBlockInfoBase
      QCryptoBlockInfoQCow
      QCryptoBlockOpenOptions
      QCryptoBlockOptionsBase
      QCryptoBlockOptionsLUKS
      QCryptoBlockOptionsQCow
      QCryptoSecretFormat
      QCryptoTLSCredsEndpoint
      QapiErrorClass
      ReplayMode
      X86CPUFeatureWordInfo
      X86CPURegister32

  Generating documentation for internal types might be useful, but
  letting them pollute QMP interface documentation isn't.  Needs fixing
  before we release.  Until then, needs a FIXME comment in qapi2texi.py.

* Union support is lacking

  The doc string language is adequate for documenting commands, events,
  and non-union types.  It doesn't really handle union variants.  Hardly
  surprising, as you fitted the language do existing practice, and
  existing (mal-)practice is neglecting to document union variant
  members.

* Documentation is lacking

  See review of qapi-code-gen.txt below.

* Doc comment error message positions are imprecise

  They always point to the beginning of the comment.

* Probably more

  We should update this with noteworthy findings during review.  I
  tried, but I suspect I missed some.


ok



> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
[Lengthy diffstat snipped...]
>
> diff --git a/tests/Makefile.include b/tests/Makefile.include
> index e98d3b6..f16764c 100644
> --- a/tests/Makefile.include
> +++ b/tests/Makefile.include
> @@ -350,6 +350,21 @@ qapi-schema += base-cycle-direct.json
>  qapi-schema += base-cycle-indirect.json
>  qapi-schema += command-int.json
>  qapi-schema += comments.json
> +qapi-schema += doc-bad-args.json
> +qapi-schema += doc-bad-symbol.json
> +qapi-schema += doc-duplicated-arg.json
> +qapi-schema += doc-duplicated-return.json
> +qapi-schema += doc-duplicated-since.json
> +qapi-schema += doc-empty-arg.json
> +qapi-schema += doc-empty-section.json
> +qapi-schema += doc-empty-symbol.json
> +qapi-schema += doc-invalid-end.json
> +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-expr.json
> +qapi-schema += doc-missing-space.json
>  qapi-schema += double-data.json
>  qapi-schema += double-type.json
>  qapi-schema += duplicate-key.json
> @@ -443,6 +458,8 @@ qapi-schema += union-optional-branch.json
>  qapi-schema += union-unknown.json
>  qapi-schema += unknown-escape.json
>  qapi-schema += unknown-expr-key.json
> +
> +
>  check-qapi-schema-y := $(addprefix tests/qapi-schema/, $(qapi-schema))
>
>  GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h \
> diff --git a/scripts/qapi.py b/scripts/qapi.py
> index 4d1b0e4..1b456b4 100644
> --- a/scripts/qapi.py
> +++ b/scripts/qapi.py
> @@ -122,6 +122,109 @@ class QAPILineError(Exception):
>              "%s:%d: %s" % (self.info['file'], self.info['line'],
self.msg)
>
>
> +class QAPIDoc(object):
> +    class Section(object):
> +        def __init__(self, name=""):

name=None feels more natural than "" for an absent optional name.


ok



> +            # optional section name (argument/member or section name)
> +            self.name = name
> +            # the list of strings for this section

Would "list of lines" be more accurate, or less?


more accurate, ok



> +            self.content = []
> +
> +        def append(self, line):
> +            self.content.append(line)
> +
> +        def __repr__(self):
> +            return "\n".join(self.content).strip()
> +
> +    class ArgSection(Section):
> +        pass

Begs the question what makes ArgSection differ from Section.  I think
it's the semantics of self.name: an ArgSection's name is the parameter
name, a non-ArgSection can either be a "meta" section (name is one of
"Returns"", "Since", "Note", "Notes", "Example", "Examples") or an
anonymous section (name is "").


yes, and the section type is checked in  _append_freeform()

> +
> +    def __init__(self, parser):
> +        self.parser = parser
> +        self.symbol = None
> +        self.body = QAPIDoc.Section()
> +        # a dict {'arg': ArgSection, ...}

Works.  Alternatevely:

           # dict mapping parameter name to ArgSection


ok


> +        self.args = OrderedDict()
> +        # a list of Section
> +        self.meta = []
> +        # the current section
> +        self.section = self.body
> +        # associated expression and info (set by expression parser)

"will be set by expression parser", or "to be set by expression parser"?


ok


> +        self.expr = None
> +        self.info = None

Clearer now.  The one remark I still have is on the use of "meta" isn't
obvious here.  I think a comment further up explaining the different
kinds of sections would help.

To be honest, I don't get what's "meta" about the "meta" sections :)


 Let's drop that distinction, it isn't necessary anymore


> +
> +    def has_meta(self, name):
> +        """Returns True if the doc has a meta section 'name'"""

PEP257[1] demands imperative mood, and punctuation:

           """Return True if the doc has a meta section 'name'."""

Unfortunately, there seems to be no established convention for marking
parameter names.  You put this one in single quotes, which works here,
but could be confusing in other cases.  I'd sidestep it:

           """Return True if we have a meta section with this name."""

or

           """Does a meta section with this name exist?"""


ok



> +        for i in self.meta:
> +            if i.name == name:
> +                return True
> +        return False
> +
> +    def append(self, line):
> +        """Adds a # comment line, to be parsed and added to current
section"""

Imperative mood:

    """Add a comment line, to be parsed and added to the current section."""

However, we're not always adding to the current section, we can also
start a new one.  The following avoids suggesting anything about the
current section:

    """Parse a comment line and add it to the documentation."""

When the function name is a verb, and its doc string starts with a
different verb, the name might be suboptimal.  Use your judgement.

If this one-liner is too terse, we can try a multi-line doc string:

    """Parse a comment line and add it to the documentation.

    TODO should we tell more about how we parse?

    Args:
        line: the comment starting with '#', with the newline stripped.

    Raises:
        QAPISchemaError: TODO explain error conditions
    """

Format stolen from the Google Python Style Guide[2], because PEP257 is
of not much help there.

Once you start with such elaborate doc strings, pressure will likely
mount to do the same elsewhere.  Could be a distraction right now.  Use
your judgement.


ok, thanks


> +        line = line[1:]
> +        if not line:
> +            self._append_freeform(line)
> +            return
> +
> +        if line[0] != ' ':
> +            raise QAPISchemaError(self.parser, "missing space after #")
> +        line = line[1:]

QAPISchemaError takes the error position from its QAPISchemaParser
argument.  In other words, the error position depends on the state of
the parser when it calls this function.  Turns out the position is the
beginning of the comment.  Servicable here.  Action at a distance,
though.

Less strict:

           # strip leading # and space
           line = line[1:]
           if line[0] == ' ':
               line = line[1:]

Also avoids special-casing empty comments.


That would raise "IndexError: string index out of range" on empty comment
lines ("#")

Also I'd like to keep the error in case a space is missing (it caught some
already).


Perhaps we should treat all leading whitespace the same (I'm not sure):

           # strip leading # and whitespace
           line = line[1:]
           line = line.lstrip()


That would break indentation in Example sections



> +
> +        if self.symbol:
> +            self._append_symbol_line(line)
> +        elif (not self.body.content and
> +              line.startswith("@") and line.endswith(":")):
> +            self.symbol = line[1:-1]
> +            if not self.symbol:
> +                raise QAPISchemaError(self.parser, "Invalid symbol")

Doesn't recognize the symbol when there's anything other than a single
space between # and @.  Pathological case: '#\t@foo:' starting in column
6 looks exactly like '# @foo:', but doesn't work.  Fortunately, your
code rejects the tab there.  Assigning meaning to (leading) whitespace
may make sense, but it must be documented clearly.


I think it's simpler to only accept a simple space (# + space + @ + symbol
+ :), it will also lead to more uniform doc.


Doesn't recognize the symbol when there's crap after ":".  Confusing
when it's invisible crap, i.e. trailing whitespace.  In general, it's
best to parse left to right, like this:

           elif not self.body.content and line.startswith('@'):
               match = valid_name.match(line[1:]);
               if not match:
                   raise QAPISchemaError(self.parser, 'Invalid name')
               if line.startswith(':', match.end(0)):
                   raise QAPISchemaError(self.parser, 'Expected ":")


Easier is simply to check :
if not line.endswith(":"):
                raise QAPIParseError(self.parser, "Line should end with :")

test added

Here, the error position is clearly suboptimal: it's the beginning of
the comment instead of where the actual error is, namely the beginning /
the end of the name.  More of the same below.  The root cause is
stacking parsers.  I'm not asking you to do anything about it at this
time.

Note my use of "name" rather than "symbol" in the error message.
qapi-code-gen.txt talks about names, not symbols.  Consistent use of
terminology matters.


Agree, isn't "symbol" more appropriate for documentation though?


> +        else:
> +            self._append_freeform(line)
> +
> +    def _append_symbol_line(self, line):
> +        name = line.split(' ', 1)[0]
> +
> +        if name.startswith("@") and name.endswith(":"):
> +            line = line[len(name)+1:]
> +            self._start_args_section(name[1:-1])

Similar issue.  Left to right:

           if re.match(r'\s*@', line):
               match = valid_name.match(line[1:]);
               if not match:
                   raise QAPISchemaError(self.parser, 'Invalid parameter
name')
               line = line[match.end(0):]
               if line.startswith(':'):
                   raise QAPISchemaError(self.parser, 'Expected ":")
               line = line[1:]
               self._start_args_section(match.group(0))

Even better would be factoring out the common code to parse '@foo:'.


It's a valid case to have a section freeform comment starting with @foo, ex:

@param: do this and than if
               @param2 is True

 I don't think it's worth to factor out the code to parse @foo vs @foo:, yet



> +        elif name in ("Returns:", "Since:",
> +                      # those are often singular or plural
> +                      "Note:", "Notes:",
> +                      "Example:", "Examples:"):
> +            line = line[len(name)+1:]
> +            self._start_meta_section(name[:-1])

Since we're re.match()ing already, here's how do to it that way:


I don't follow your suggestion for the reason above,so I still have the
first word 'name'


           else
               match = re.match(r'\s*(Returns|Since|Notes?|Examples?):',
line)
               if match:
                   line = line[match.end(0):]
                   self._start_meta_section(match.group(1))


not really much nicer to me (especially because no match yet), I'll keep
the current code for now



> +
> +        self._append_freeform(line)
> +
> +    def _start_args_section(self, name):
> +        if not name:
> +            raise QAPISchemaError(self.parser, "Invalid argument name")

parameter name


ok



> +        if name in self.args:
> +            raise QAPISchemaError(self.parser, "'%s' arg duplicated" %
name)

Duplicate parameter name


ok


> +        self.section = QAPIDoc.ArgSection(name)
> +        self.args[name] = self.section
> +
> +    def _start_meta_section(self, name):
> +        if name in ("Returns", "Since") and self.has_meta(name):
> +            raise QAPISchemaError(self.parser,
> +                                  "Duplicated '%s' section" % name)
> +        self.section = QAPIDoc.Section(name)
> +        self.meta.append(self.section)
> +
> +    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] \
> +           and line and not line[0].isspace():

PEP8[3] prefers parenthesises over backslash:

           if (in_arg and self.section.content and not
self.section.content[-1]
               and line and not line[0].isspace()):


yes, thanks


> +            # an empty line followed by a non-indented
> +            # comment ends the argument section
> +            self.section = self.body
> +            self._append_freeform(line)
> +            return

Switching back to the self.body section like this reorders the
documentation text.  I still think this is a terrible idea.  A dumb
script is exceedingly unlikely to improve human-written doc comment text
by reordering.  In the rare case it does, the doc comment source should
be reordered.

Here's an example where the doc generator happily creates unintelligible
garbage if I format CpuDefinitionInfo's doc comment in a slightly off
way:

    ##
    # @CpuDefinitionInfo:
    #
    # Virtual CPU definition.
    #
    # @name: the name of the CPU definition
    #
    # @migration-safe: #optional whether a CPU definition can be safely
    # used for migration in combination with a QEMU compatibility
    # machine when migrating between different QMU versions and between
    # hosts with different sets of (hardware or software)
    # capabilities.
    #
    # If not provided, information is not available and callers should
    # not assume the CPU definition to be migration-safe. (since 2.8)
    #
    # @static: whether a CPU definition is static and will not change
    # depending on QEMU version, machine type, machine options and
    # accelerator options.  A static model is always
    # migration-safe. (since 2.8)
    #
    # @unavailable-features: #optional List of properties that prevent
    # the CPU model from running in the current host. (since 2.8)
    #
    # @unavailable-features is a list of QOM property names that
    # represent CPU model attributes that prevent the CPU from running.
    # If the QOM property is read-only, that means there's no known
    # way to make the CPU model run in the current host. Implementations
    # that choose not to provide specific information return the
    # property name "type".
    #
    # If the property is read-write, it means that it MAY be possible
    # to run the CPU model in the current host if that property is
    # changed. Management software can use it as hints to suggest or
    # choose an alternative for the user, or just to generate meaningful
    # error messages explaining why the CPU model can't be used.
    # If @unavailable-features is an empty list, the CPU model is
    # runnable using the current host and machine-type.
    # If @unavailable-features is not present, runnability
    # information for the CPU is not available.
    #
    # Since: 1.2.0
    ##
    { 'struct': 'CpuDefinitionInfo',
      'data': { 'name': 'str', '*migration-safe': 'bool', 'static': 'bool',
                '*unavailable-features': [ 'str' ] } }

To detect the problem, you have to read the generated docs attentively.
You know my opinion on our chances for that to happen during
development.

My point is not that we should support this doc comment format.  My
point is that people could conceivably write something like it, and not
get caught in patch review.

I can see three ways out of this swamp:

1. Let sections continue until another one begins.


but we have interleaved  sections, and no explicit "body" section tag, ex:

##
# @input-send-event:
#
# Send input event(s) to guest.
#
# @device: #optional display device to send event(s) to.
# @head: #optional head to send event(s) to, in case the
#        display device supports multiple scanouts.
# @events: List of InputEvent union.
#
# Returns: Nothing on success.
#
# The @device and @head parameters can be used to send the input event
# to specific input devices in case (a) multiple input devices of the
# same kind are added to the virtual machine and (b) you have
# configured input routing (see docs/multiseat.txt) for those input
# ....

3. When a section ends, start a new anonymous section.


It's not clear how to recognize when a section ends and append to comment
body.



2. Make text between sections an error.


Sound like the best option to me. I'll fix the doc and add a check & test
for this common pattern though:

##
# @TestStruct:
#
# body with @var
#
# @integer: foo
#           blah
#
#           bao
#
# Let's catch this bad comment.
##




Can't say offhand which one will work best.


> +        if in_arg or not self.section.name.startswith("Example"):
> +            line = line.strip()

Stripping whitespace is not "Markdown-like", because intendation carries
meaning in Markdown.  Example:

* First item in itemized list
* Second item
    * Sub-item of second item
    * Another sub-item
* Third item

Stripping whitespace destroys the list structure.  If that's what you
want, you get to document where your "Markdown-like" markup is unlike
Markdown :)


let's use "comment annotations" instead



Is there a technical reason for stripping whitespace?

See also discussion of space after # above.

> +        self.section.append(line)
> +
> +
>  class QAPISchemaParser(object):
>
>      def __init__(self, fp, previously_included=[], incl_info=None):
> @@ -137,11 +240,18 @@ class QAPISchemaParser(object):
>          self.line = 1
>          self.line_pos = 0
>          self.exprs = []
> +        self.docs = []
>          self.accept()
>
>          while self.tok is not None:
>              info = {'file': fname, 'line': self.line,
>                      'parent': self.incl_info}
> +            if self.tok == '#' and self.val.startswith('##'):

How can self.val *not* start with '##' here?


 you are right, unnecessary condition since we break get_expr() only in
this case now


> +                doc = self.get_doc()
> +                doc.info = info

Let's pass info as argument to get_doc(), so we don't have to dot into
doc here.  get_doc() can avoid dotting into doc by passing info to the
constructor.


ok


> +                self.docs.append(doc)
> +                continue
> +
>              expr = self.get_expr(False)
>              if isinstance(expr, dict) and "include" in expr:
>                  if len(expr) != 1:
> @@ -160,6 +270,7 @@ class QAPISchemaParser(object):
>                          raise QAPILineError(info, "Inclusion loop for %s"
>                                              % include)
>                      inf = inf['parent']
> +
>                  # skip multiple include of the same file
>                  if incl_abs_fname in previously_included:
>                      continue
> @@ -171,12 +282,38 @@ class QAPISchemaParser(object):
>                  exprs_include = QAPISchemaParser(fobj,
previously_included,
>                                                   info)
>                  self.exprs.extend(exprs_include.exprs)
> +                self.docs.extend(exprs_include.docs)
>              else:
>                  expr_elem = {'expr': expr,
>                               'info': info}
> +                if self.docs and not self.docs[-1].expr:
> +                    self.docs[-1].expr = expr
> +                    expr_elem['doc'] = self.docs[-1]
> +

Attaches the expression to the last doc comment that doesn't already
have one.  A bit sloppy, because there could be non-doc comments in
between, or the doc comment could be in another file.  It'll do, at
least for now.


I extended the condition to check it attaches the doc from the same file



>                  self.exprs.append(expr_elem)
>
> -    def accept(self):
> +    def get_doc(self):
> +        if self.val != '##':
> +            raise QAPISchemaError(self, "Junk after '##' at start of "
> +                                  "documentation comment")
> +
> +        doc = QAPIDoc(self)
> +        self.accept(False)
> +        while self.tok == '#':
> +            if self.val.startswith('##'):
> +                # End of doc comment
> +                if self.val != '##':
> +                    raise QAPISchemaError(self, "Junk after '##' at end
of "
> +                                          "documentation comment")
> +                self.accept()
> +                return doc
> +            else:
> +                doc.append(self.val)
> +            self.accept(False)
> +
> +        raise QAPISchemaError(self, "Documentation comment must end with
'##'")

Let's put this after accept, next to the other get_FOO().


ok



> +
> +    def accept(self, skip_comment=True):
>          while True:
>              self.tok = self.src[self.cursor]
>              self.pos = self.cursor
> @@ -184,7 +321,13 @@ class QAPISchemaParser(object):
>              self.val = None
>
>              if self.tok == '#':
> +                if self.src[self.cursor] == '#':
> +                    # Start of doc comment
> +                    skip_comment = False
>                  self.cursor = self.src.find('\n', self.cursor)
> +                if not skip_comment:
> +                    self.val = self.src[self.pos:self.cursor]
> +                    return
>              elif self.tok in "{}:,[]":
>                  return
>              elif self.tok == "'":

Copied from review of v3, so I don't forget:

Comment tokens are thrown away as before, except when the parser asks
for them by passing skip_comment=False, or when the comment token starts
with ##.  The parser asks while parsing a doc comment, in get_doc().

This is a backchannel from the parser to the lexer.  I'd rather avoid
such lexer hacks, but I guess we can address that on top.

A comment starting with ## inside an expression is now a syntax error.
For instance, input

    {
    ##

yields

    /dev/stdin:2:1: Expected string or "}"

Rather unfriendly error message, but we can fix that on top.


I don't have a good solution to that


> @@ -713,7 +856,7 @@ def check_keys(expr_elem, meta, required,
optional=[]):
>                                  % (key, meta, name))
>
>
> -def check_exprs(exprs):
> +def check_exprs(exprs, strict_doc):

Note: strict_doc=False unless this is qapi2texi.py.


For tests reasons: we may want to fix the test instead, or have a flag
QAPI_CHECK/NOSTRICT_DOC=1 or an option.


>      global all_names
>
>      # Learn the types and check for valid expression keys
> @@ -722,6 +865,11 @@ def check_exprs(exprs):
>      for expr_elem in exprs:
>          expr = expr_elem['expr']
>          info = expr_elem['info']
> +
> +        if strict_doc and 'doc' not in expr_elem:
> +            raise QAPILineError(info,
> +                                "Expression missing documentation
comment")
> +

Why should we supress this error in programs other than qapi2texi.py?


because the tests don't have comments


Can't see a test for this one.


because tests aren't strict :)

I guess you'll tell me to fix the tests instead, so I did that in the next
series.


>          if 'enum' in expr:
>              check_keys(expr_elem, 'enum', ['data'], ['prefix'])
>              add_enum(expr['enum'], info, expr['data'])
> @@ -780,6 +928,63 @@ def check_exprs(exprs):
>      return exprs
>
>
> +def check_simple_doc(doc):

You call this "free-form" elsewhere.  Pick one name and stick to it.
I think free-form is more descriptive than simple.

ok


> +    if doc.symbol:
> +        raise QAPILineError(doc.info,
> +                            "'%s' documention is not followed by the
definition"
> +                            % doc.symbol)

"Documentation for %s is not ..."


ok


> +
> +    body = str(doc.body)
> +    if re.search(r'@\S+:', body, re.MULTILINE):
> +        raise QAPILineError(doc.info,
> +                            "Document body cannot contain @NAME:
sections")
> +
> +
> +def check_expr_doc(doc, expr, info):

You call this "symbol" elsewhere.  I think "definition" would be better
than either.


 ok


> +    for i in ('enum', 'union', 'alternate', 'struct', 'command',
'event'):
> +        if i in expr:
> +            meta = i
> +            break
> +
> +    name = expr[meta]
> +    if doc.symbol != name:
> +        raise QAPILineError(info, "Definition of '%s' follows
documentation"
> +                            " for '%s'" % (name, doc.symbol))
> +    if doc.has_meta('Returns') and 'command' not in expr:
> +        raise QAPILineError(info, "Invalid return documentation")

Suggest something like "'Returns:' is only valid for commands".

We accept 'Returns:' even when the command doesn't return anything,
because we currently use it to document errors, too.  Can't say I like
that, but it's out of scope here.


ok



> +
> +    doc_args = set(doc.args.keys())
> +    if meta == 'union':
> +        data = expr.get('base', [])
> +    else:
> +        data = expr.get('data', [])
> +    if isinstance(data, dict):
> +        data = data.keys()
> +    args = set([name.strip('*') for name in data])

Not fixed since v3:

* Flat union where 'base' is a string, e.g. union UserDefFlatUnion in
  qapi-schema-test.json has base 'UserDefUnionBase', args is set(['a',
  'B', 'e', 'D', 'f', 'i', 'o', 'n', 's', 'r', 'U'])

* Command where 'data' is a string, e.g. user_def_cmd0 in
  qapi-schema-test.json has data 'Empty2', args is set(['E', 'm', 'p',
  '2', 't', 'y'])


* Event where 'data' is a string, no test case handy (hole in test
  coverage)


ok, I changed it that way, that fixes it:
+    if isinstance(data, list):
+        args = set([name.strip('*') for name in data])
+    else:
+        args = set()



> +    if meta == 'alternate' or \
> +       (meta == 'union' and not expr.get('discriminator')):
> +        args.add('type')

As explained in review of v3, this is only a subset of the real set of
members.  Computing the exact set is impractical when working with the
abstract syntax tree.  I believe we'll eventually have to rewrite this
code to work with the QAPISchemaEntity instead.


I don't think we want to list all the members as this would lead to
duplicated documentation. Instead it should document only the members of
the expr being defined. In which case, it looks like this check is good
enough, no?



> +    if not doc_args.issubset(args):
> +        raise QAPILineError(info, "Members documentation is not a subset
of"
> +                            " API %r > %r" % (list(doc_args),
list(args)))
> +
> +
> +def check_docs(docs):
> +    for doc in docs:
> +        for section in doc.args.values() + doc.meta:
> +            content = str(section)
> +            if not content or content.isspace():
> +                raise QAPILineError(doc.info,
> +                                    "Empty doc section '%s'" %
section.name)
> +
> +        if not doc.expr:
> +            check_simple_doc(doc)
> +        else:
> +            check_expr_doc(doc, doc.expr, doc.info)
> +
> +    return docs
> +
> +
>  #
>  # Schema compiler frontend
>  #
> @@ -1249,9 +1454,11 @@ class QAPISchemaEvent(QAPISchemaEntity):
>
>
>  class QAPISchema(object):
> -    def __init__(self, fname):
> +    def __init__(self, fname, strict_doc=False):
>          try:
> -            self.exprs = check_exprs(QAPISchemaParser(open(fname,
"r")).exprs)
> +            parser = QAPISchemaParser(open(fname, "r"))
> +            self.exprs = check_exprs(parser.exprs, strict_doc)
> +            self.docs = check_docs(parser.docs)
>              self._entity_dict = {}
>              self._predefining = True
>              self._def_predefineds()
> diff --git a/scripts/qapi2texi.py b/scripts/qapi2texi.py
> new file mode 100755
> index 0000000..0cec43a
> --- /dev/null
> +++ b/scripts/qapi2texi.py

Still only skimming this one.

> @@ -0,0 +1,331 @@
> +#!/usr/bin/env python
> +# QAPI texi generator
> +#
> +# This work is licensed under the terms of the GNU LGPL, version 2+.
> +# See the COPYING file in the top-level directory.
> +"""This script produces the documentation of a qapi schema in texinfo
format"""
> +import re
> +import sys
> +
> +import qapi
> +
> +COMMAND_FMT = """
> +@deftypefn {type} {{{ret}}} {name} @
> +{{{args}}}
> +
> +{body}
> +
> +@end deftypefn
> +
> +""".format
> +
> +ENUM_FMT = """
> +@deftp Enum {name}
> +
> +{body}
> +
> +@end deftp
> +
> +""".format
> +
> +STRUCT_FMT = """
> +@deftp {{{type}}} {name} @
> +{{{attrs}}}
> +
> +{body}
> +
> +@end deftp
> +
> +""".format
> +
> +EXAMPLE_FMT = """@example
> +{code}
> +@end example
> +""".format
> +
> +
> +def subst_strong(doc):
> +    """Replaces *foo* by @strong{foo}"""
> +    return re.sub(r'\*([^_\n]+)\*', r'@emph{\1}', doc)
> +
> +
> +def subst_emph(doc):
> +    """Replaces _foo_ by @emph{foo}"""
> +    return re.sub(r'\s_([^_\n]+)_\s', r' @emph{\1} ', doc)
> +
> +
> +def subst_vars(doc):
> +    """Replaces @var by @code{var}"""
> +    return re.sub(r'@([\w-]+)', r'@code{\1}', doc)
> +
> +
> +def subst_braces(doc):
> +    """Replaces {} with @{ @}"""
> +    return doc.replace("{", "@{").replace("}", "@}")
> +
> +
> +def texi_example(doc):
> +    """Format @example"""
> +    doc = subst_braces(doc).strip('\n')
> +    return EXAMPLE_FMT(code=doc)
> +
> +
> +def texi_comment(doc):
> +    """
> +    Format a comment
> +
> +    Lines starting with:
> +    - |: generates an @example
> +    - =: generates @section
> +    - ==: generates @subsection
> +    - 1. or 1): generates an @enumerate @item
> +    - o/*/-: generates an @itemize list
> +    """
> +    lines = []
> +    doc = subst_braces(doc)
> +    doc = subst_vars(doc)
> +    doc = subst_emph(doc)
> +    doc = subst_strong(doc)
> +    inlist = ""
> +    lastempty = False
> +    for line in doc.split('\n'):
> +        empty = line == ""
> +
> +        if line.startswith("| "):
> +            line = EXAMPLE_FMT(code=line[2:])
> +        elif line.startswith("= "):
> +            line = "@section " + line[2:]
> +        elif line.startswith("== "):
> +            line = "@subsection " + line[3:]
> +        elif re.match("^([0-9]*[.)]) ", line):
> +            if not inlist:
> +                lines.append("@enumerate")
> +                inlist = "enumerate"
> +            line = line[line.find(" ")+1:]
> +            lines.append("@item")
> +        elif re.match("^[o*-] ", line):
> +            if not inlist:
> +                lines.append("@itemize %s" % {'o': "@bullet",
> +                                              '*': "@minus",
> +                                              '-': ""}[line[0]])
> +                inlist = "itemize"
> +            lines.append("@item")
> +            line = line[2:]
> +        elif lastempty and inlist:
> +            lines.append("@end %s\n" % inlist)
> +            inlist = ""
> +
> +        lastempty = empty
> +        lines.append(line)
> +
> +    if inlist:
> +        lines.append("@end %s\n" % inlist)
> +    return "\n".join(lines)
> +
> +
> +def texi_args(expr, key="data"):
> +    """
> +    Format the functions/structure/events.. arguments/members
> +    """
> +    if key not in expr:
> +        return ""
> +
> +    args = expr[key]
> +    arg_list = []
> +    if isinstance(args, str):
> +        arg_list.append(args)
> +    else:
> +        for name, typ in args.iteritems():
> +            # optional arg
> +            if name.startswith("*"):
> +                name = name[1:]
> +                arg_list.append("['%s': @var{%s}]" % (name, typ))
> +            # regular arg
> +            else:
> +                arg_list.append("'%s': @var{%s}" % (name, typ))

Inappropriate use of @var.  @var is for metasyntactic variables,
i.e. something that stands for another piece of text.  typ isn't, it's
the name of a specific QAPI type.  I think you should use @code.


yes


This is the reason why the type names in qemu-qmp-ref.txt are often
mangled, e.g.

 -- Struct: VersionInfo { 'qemu': VERSIONTRIPLE, 'package': STR }


Right, we have format issue here. If we change it for @code, we get
additional quotes in the text format. The simpler is to use no format or
@t{}



> +
> +    return ", ".join(arg_list)
> +
> +
> +def texi_body(doc):
> +    """
> +    Format the body of a symbol documentation:
> +    - a table of arguments
> +    - followed by "Returns/Notes/Since/Example" sections
> +    """
> +    def _section_order(section):
> +        return {"Returns": 0,
> +                "Note": 1,
> +                "Notes": 1,
> +                "Since": 2,
> +                "Example": 3,
> +                "Examples": 3}[section]
> +
> +    body = "@table @asis\n"
> +    for arg, section in doc.args.iteritems():
> +        desc = str(section)
> +        opt = ''
> +        if desc.startswith("#optional"):
> +            desc = desc[10:]
> +            opt = ' *'
> +        elif desc.endswith("#optional"):
> +            desc = desc[:-10]
> +            opt = ' *'
> +        body += "@item @code{'%s'}%s\n%s\n" % (arg, opt,
texi_comment(desc))
> +    body += "@end table\n"
> +    body += texi_comment(str(doc.body))
> +
> +    meta = sorted(doc.meta, key=lambda i: _section_order(i.name))
> +    for section in meta:
> +        name, doc = (section.name, str(section))
> +        func = texi_comment
> +        if name.startswith("Example"):
> +            func = texi_example
> +
> +        body += "\n@quotation %s\n%s\n@end quotation" % \
> +                (name, func(doc))
> +    return body
> +
> +
> +def texi_alternate(expr, doc):
> +    """
> +    Format an alternate to texi
> +    """
> +    args = texi_args(expr)
> +    body = texi_body(doc)
> +    return STRUCT_FMT(type="Alternate",
> +                      name=doc.symbol,
> +                      attrs="[ " + args + " ]",
> +                      body=body)
> +
> +
> +def texi_union(expr, doc):
> +    """
> +    Format an union to texi

I think it's "a union".

ok


> +    """
> +    attrs = "@{ " + texi_args(expr, "base") + " @}"
> +    discriminator = expr.get("discriminator")
> +    if not discriminator:
> +        union = "Flat Union"
> +        discriminator = "type"
> +    else:
> +        union = "Union"

Condition is backwards.


fixed


> +    attrs += " + '%s' = [ " % discriminator
> +    attrs += texi_args(expr, "data") + " ]"
> +    body = texi_body(doc)
> +
> +    return STRUCT_FMT(type=union,
> +                      name=doc.symbol,
> +                      attrs=attrs,
> +                      body=body)

You're inventing syntax here.  Example output:

 -- Union: QCryptoBlockOpenOptions { QCryptoBlockOptionsBase } +
          'format' = [ 'qcow': QCRYPTOBLOCKOPTIONSQCOW, 'luks':
          QCRYPTOBLOCKOPTIONSLUKS ]

     The options that are available for all encryption formats when
     opening an existing volume
          Since: 2.6

 -- Flat Union: ImageInfoSpecific { } + 'type' = [ 'qcow2':
          IMAGEINFOSPECIFICQCOW2, 'vmdk': IMAGEINFOSPECIFICVMDK, 'luks':
          QCRYPTOBLOCKINFOLUKS ]

     A discriminated record of image format specific information
     structures.
          Since: 1.7

Note that QCryptoBlockOpenOptions is actually a flat union, and
ImageInfoSpecific a simple union.  As I said, the condition is
backwards.

The meaning of the attrs part is unobvious.  Familiarity with schema
syntax doesn't really help.

Could we simply use schema syntax here?


Union: QCryptoBlockOpenOptions {
'base': QCryptoBlockOptionsBase,
'discriminator': 'format',
'data':  { 'qcow': QCryptoBlockOptionsQCow, 'luks':
QCryptoBlockCreateOptionsLUKS }

}

Doesn't look obvious either and pollute the documentation with
schema-specific parameters.

If not: whatever format you use, you first need to explain it.


I am not sure my solution is the best and will remain, but ok let's try to
document it for now.



> +
> +
> +def texi_enum(expr, doc):
> +    """
> +    Format an enum to texi
> +    """
> +    for i in expr['data']:
> +        if i not in doc.args:
> +            doc.args[i] = ''
> +    body = texi_body(doc)
> +    return ENUM_FMT(name=doc.symbol,
> +                    body=body)
> +
> +
> +def texi_struct(expr, doc):
> +    """
> +    Format a struct to texi
> +    """
> +    args = texi_args(expr)
> +    body = texi_body(doc)
> +    attrs = "@{ " + args + " @}"
> +    base = expr.get("base")
> +    if base:
> +        attrs += " + %s" % base
> +    return STRUCT_FMT(type="Struct",
> +                      name=doc.symbol,
> +                      attrs=attrs,
> +                      body=body)

More syntax invention.  Example output:

 -- Struct: BlockdevOptionsReplication { 'mode': REPLICATIONMODE,
          ['top-id': STR] } + BlockdevOptionsGenericFormat

     ''mode''
          the replication mode
     ''top-id'' *
          In secondary mode, node name or device ID of the root node who
          owns the replication node chain.  Must not be given in primary
          mode.
     Driver specific block device options for replication
          Since: 2.8

Meaning of the attrs part is perhaps more guessable here, but it's still
guesswork.

The meaning of * after ''top-id'' is also unobvious.

Note the redundancy between the attrs part and the body: both state
member names and optionalness.  The body doesn't state member types and
base type.  If we fixed that, we could drop the attrs part and save us
the trouble of defining and explaining a syntax for it.

Let me take a step back.  This document is about the QMP wire format.
There are no such things as simple and flat unions on the wire, only
JSON objects.  QMP introspection duly describes a type's JSON objects,
not how it's defined in QAPI.  I think QMP documentation should ideally
do the same.

QMP introspection uses a common structure for struct, simple and flat
union: common members, variants, and if variants, then the common member
that serves as tag.  See introspect.json for details.

Base types are flattened away.  Appropriate for introspection, but
documentation shouldn't do that.

I wrote "ideally" because it's probably too big a step.  I'm willing to
settle for something less than ideal.


I don't have clear idea what to do here, so if we can leave that for later,
that would be nice for me (I am already spending more time than I imagined
I would on doc stuff)



> +
> +
> +def texi_command(expr, doc):
> +    """
> +    Format a command to texi
> +    """
> +    args = texi_args(expr)
> +    ret = expr["returns"] if "returns" in expr else ""
> +    body = texi_body(doc)
> +    return COMMAND_FMT(type="Command",
> +                       name=doc.symbol,
> +                       ret=ret,
> +                       args="(" + args + ")",
> +                       body=body)
> +
> +
> +def texi_event(expr, doc):
> +    """
> +    Format an event to texi
> +    """
> +    args = texi_args(expr)
> +    body = texi_body(doc)
> +    return COMMAND_FMT(type="Event",
> +                       name=doc.symbol,
> +                       ret="",
> +                       args="(" + args + ")",
> +                       body=body)
> +
> +
> +def texi_expr(expr, doc):
> +    """
> +    Format an expr to texi
> +    """
> +    (kind, _) = expr.items()[0]
> +
> +    fmt = {"command": texi_command,
> +           "struct": texi_struct,
> +           "enum": texi_enum,
> +           "union": texi_union,
> +           "alternate": texi_alternate,
> +           "event": texi_event}
> +    try:
> +        fmt = fmt[kind]
> +    except KeyError:
> +        raise ValueError("Unknown expression kind '%s'" % kind)

The try / except converts one kind of error into another.  What does
that buy us?  As far as I can tell, this shouldn't ever happen.


dropped


> +
> +    return fmt(expr, doc)
> +
> +
> +def texi(docs):
> +    """
> +    Convert QAPI schema expressions to texi documentation
> +    """
> +    res = []
> +    for doc in docs:
> +        expr = doc.expr
> +        if not expr:
> +            res.append(texi_body(doc))
> +            continue
> +        try:
> +            doc = texi_expr(expr, doc)
> +            res.append(doc)
> +        except:
> +            print >>sys.stderr, "error at @%s" % doc.info
> +            raise
> +
> +    return '\n'.join(res)
> +
> +
> +def main(argv):
> +    """
> +    Takes schema argument, prints result to stdout
> +    """
> +    if len(argv) != 2:
> +        print >>sys.stderr, "%s: need exactly 1 argument: SCHEMA" %
argv[0]
> +        sys.exit(1)
> +
> +    schema = qapi.QAPISchema(argv[1], strict_doc=True)
> +    print texi(schema.docs)
> +
> +
> +if __name__ == "__main__":
> +    main(sys.argv)
> diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
> index 2841c51..8bc963e 100644
> --- a/docs/qapi-code-gen.txt
> +++ b/docs/qapi-code-gen.txt
> @@ -45,16 +45,13 @@ QAPI parser does not).  At present, there is no place
where a QAPI
>  schema requires the use of JSON numbers or null.
>
>  Comments are allowed; anything between an unquoted # and the following
> -newline is ignored.  Although there is not yet a documentation
> -generator, a form of stylized comments has developed for consistently
> -documenting details about an expression and when it was added to the
> -schema.  The documentation is delimited between two lines of ##, then
> -the first line names the expression, an optional overview is provided,
> -then individual documentation about each member of 'data' is provided,
> -and finally, a 'Since: x.y.z' tag lists the release that introduced
> -the expression.  Optional members are tagged with the phrase
> -'#optional', often with their default value; and extensions added
> -after the expression was first released are also given a '(since
> +newline is ignored.  The documentation is delimited between two lines
> +of ##, then the first line names the expression, an optional overview
> +is provided, then individual documentation about each member of 'data'
> +is provided, and finally, a 'Since: x.y.z' tag lists the release that
> +introduced the expression.  Optional members are tagged with the
> +phrase '#optional', often with their default value; and extensions
> +added after the expression was first released are also given a '(since
>  x.y.z)' comment.  For example:
>
>      ##
> @@ -73,12 +70,49 @@ x.y.z)' comment.  For example:
>      #           (Since 2.0)
>      #
>      # Since: 0.14.0
> +    #
> +    # Notes: You can also make a list:
> +    #        - with items
> +    #        - like this
> +    #
> +    # Example:
> +    #
> +    # -> { "execute": ... }
> +    # <- { "return": ... }
> +    #
>      ##
>      { 'struct': 'BlockStats',
>        'data': {'*device': 'str', 'stats': 'BlockDeviceStats',
>                 '*parent': 'BlockStats',
>                 '*backing': 'BlockStats'} }

This example gives an idea of how to document struct types.  But the
reader is left guessing how to document other kinds of definitions.  In
particular, there's no mention of "Returns", "Note" and "Examples"
sections anywhere in this file.  As is, this could do for a tutorial,
but this file is a *reference*, not a tutorial.

For a reference, we need to be more thorough.  A doc comments section on
its own seems advisable.

I guess doc comment examples are best added to the schema code examples
in sections === Struct types ===, ..., === Events ===.

Careful review for completeness is advised.


So far we did quite fine with the generic example. I am not convinced we
need to have doc comments for all kinds of types, it looks redundant to me.



> +It's also possible to create documentation sections, such as:
> +
> +    ##
> +    # = Section
> +    # == Subsection
> +    #
> +    # Some text foo with *strong* and _emphasis_
> +    # 1. with a list
> +    # 2. like that
> +    #
> +    # And some code:
> +    # | $ echo foo
> +    # | -> do this
> +    # | <- get that
> +    #
> +    ##
> +
> +Text *foo* and _foo_ are for "strong" and "emphasis" styles (they do
> +not work over multiple lines). @foo is used to reference a symbol.
> +
> +Lines starting with the following characters and a space:
> +- | are examples
> +- = are top section
> +- == are subsection
> +- X. or X) are enumerations (X is any number)
> +- o/*/- are itemized list
> +

Is this doc markup documentation complete?


I think so


Is it related to any existing text markup language?  Hmm, the commit
message calls it "Markdown-like".  Should we mention that here?


I'll use "annotations" instead in the doc



Is all markup valid in all contexts?  For instance, is = Section valid
in symbol blocks?  What does | (or any markup for that matter) do within
an Example section?


Example is verbatim

I improved a bit the doc based on your feedback. I'd suggest we iterate
with additional patches later, I could use help.


>  The schema sets up a series of types, as well as commands and events
>  that will use those types.  Forward references are allowed: the parser
>  scans in two passes, where the first pass learns all type names, and
> diff --git a/tests/qapi-schema/doc-bad-args.err
b/tests/qapi-schema/doc-bad-args.err
> new file mode 100644
> index 0000000..a55e003
> --- /dev/null
> +++ b/tests/qapi-schema/doc-bad-args.err
> @@ -0,0 +1 @@
> +tests/qapi-schema/doc-bad-args.json:1: Members documentation is not a
subset of API ['a', 'b'] > ['a']
> diff --git a/tests/qapi-schema/doc-bad-args.exit
b/tests/qapi-schema/doc-bad-args.exit
> new file mode 100644
> index 0000000..d00491f
> --- /dev/null
> +++ b/tests/qapi-schema/doc-bad-args.exit
> @@ -0,0 +1 @@
> +1
> diff --git a/tests/qapi-schema/doc-bad-args.json
b/tests/qapi-schema/doc-bad-args.json
> new file mode 100644
> index 0000000..26992ea
> --- /dev/null
> +++ b/tests/qapi-schema/doc-bad-args.json
> @@ -0,0 +1,6 @@
> +##
> +# @foo:
> +# @a: a
> +# @b: b
> +##
> +{ 'command': 'foo', 'data': {'a': 'int'} }

The existing tests/qapi-schema/*.json explain what is being tested in a
comment.  Perhaps like this:

    # Arguments listed in the doc comment must exist in the actual schema

    ##
    # @foo:
    ...

Likewise for other tests.


ok



> diff --git a/tests/qapi-schema/doc-bad-args.out
b/tests/qapi-schema/doc-bad-args.out
> new file mode 100644
> index 0000000..e69de29
> diff --git a/tests/qapi-schema/doc-bad-symbol.err
b/tests/qapi-schema/doc-bad-symbol.err
> new file mode 100644
> index 0000000..9c969d1
> --- /dev/null
> +++ b/tests/qapi-schema/doc-bad-symbol.err
> @@ -0,0 +1 @@
> +tests/qapi-schema/doc-bad-symbol.json:1: Definition of 'foo' follows
documentation for 'food'
> diff --git a/tests/qapi-schema/doc-bad-symbol.exit
b/tests/qapi-schema/doc-bad-symbol.exit
> new file mode 100644
> index 0000000..d00491f
> --- /dev/null
> +++ b/tests/qapi-schema/doc-bad-symbol.exit
> @@ -0,0 +1 @@
> +1
> diff --git a/tests/qapi-schema/doc-bad-symbol.json
b/tests/qapi-schema/doc-bad-symbol.json
> new file mode 100644
> index 0000000..7255152
> --- /dev/null
> +++ b/tests/qapi-schema/doc-bad-symbol.json
> @@ -0,0 +1,4 @@
> +##
> +# @food:
> +##
> +{ 'command': 'foo', 'data': {'a': 'int'} }
> diff --git a/tests/qapi-schema/doc-bad-symbol.out
b/tests/qapi-schema/doc-bad-symbol.out
> new file mode 100644
> index 0000000..e69de29
> diff --git a/tests/qapi-schema/doc-duplicated-arg.err
b/tests/qapi-schema/doc-duplicated-arg.err
> new file mode 100644
> index 0000000..88a272b
> --- /dev/null
> +++ b/tests/qapi-schema/doc-duplicated-arg.err
> @@ -0,0 +1 @@
> +tests/qapi-schema/doc-duplicated-arg.json:4:1: 'a' arg duplicated
> diff --git a/tests/qapi-schema/doc-duplicated-arg.exit
b/tests/qapi-schema/doc-duplicated-arg.exit
> new file mode 100644
> index 0000000..d00491f
> --- /dev/null
> +++ b/tests/qapi-schema/doc-duplicated-arg.exit
> @@ -0,0 +1 @@
> +1
> diff --git a/tests/qapi-schema/doc-duplicated-arg.json
b/tests/qapi-schema/doc-duplicated-arg.json
> new file mode 100644
> index 0000000..f7804c2
> --- /dev/null
> +++ b/tests/qapi-schema/doc-duplicated-arg.json
> @@ -0,0 +1,5 @@
> +##
> +# @foo:
> +# @a:
> +# @a:
> +##
> diff --git a/tests/qapi-schema/doc-duplicated-arg.out
b/tests/qapi-schema/doc-duplicated-arg.out
> new file mode 100644
> index 0000000..e69de29
> diff --git a/tests/qapi-schema/doc-duplicated-return.err
b/tests/qapi-schema/doc-duplicated-return.err
> new file mode 100644
> index 0000000..1b02880
> --- /dev/null
> +++ b/tests/qapi-schema/doc-duplicated-return.err
> @@ -0,0 +1 @@
> +tests/qapi-schema/doc-duplicated-return.json:5:1: Duplicated 'Returns'
section

> diff --git a/tests/qapi-schema/doc-duplicated-return.exit
b/tests/qapi-schema/doc-duplicated-return.exit<br class="gmail_msg

-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v5 13/17] qapi: add qapi2texi script
  2016-12-05 17:35     ` Marc-André Lureau
@ 2016-12-06 11:50       ` Markus Armbruster
  2016-12-06 13:07         ` Marc-André Lureau
  0 siblings, 1 reply; 48+ messages in thread
From: Markus Armbruster @ 2016-12-06 11:50 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

I had to resort to diff to find your replies, and massage the text
manually to produce a readable reply myself.  Please quote the usual
way.

> Markus Armbruster <armbru@redhat.com> writes:
> 
> > Marc-André Lureau <marcandre.lureau@redhat.com> writes:
> >
> >> As the name suggests, the qapi2texi script converts JSON QAPI
> >> description into a texi file suitable for different target
> >> formats (info/man/txt/pdf/html...).
> >>
> >> It parses the following kind of blocks:
> >>
> >> Free-form:
> >>
> >>   ##
> >>   # = Section
> >>   # == Subsection
> >>   #
> >>   # Some text foo with *emphasis*
> >>   # 1. with a list
> >>   # 2. like that
> >>   #
> >>   # And some code:
> >>   # | $ echo foo
> >>   # | -> do this
> >>   # | <- get that
> >>   #
> >>   ##
> >>
> >> Symbol:
> >>
> >>   ##
> >>   # @symbol:
> >>   #
> >>   # Symbol body ditto ergo sum. Foo bar
> >>   # baz ding.
> >>   #
> >>   # @arg: foo
> >>   # @arg: #optional foo
> >
> > Let's not use @arg twice.
> >
> > Terminology: I prefer to use "parameter" for formal parameters, and
> > "argument" for actual arguments.  This matches how The Python Language
> > Reference uses the terms.
> >
> > What about
> >
> >     # @param1: the frob to frobnicate
> >     # @param2: #optional how hard to frobnicate
> 
> ok
> 
> >>   #
> >>   # Returns: returns bla bla
> >>   #          Or bla blah
> >
> > Repeating "returns" is awkward, and we don't do that in our schemas.
> >
> > We need a period before "Or", or spell it "or".
> >
> > What about
> >
> >     # Returns: the frobnicated frob.
> >     #          If frob isn't frobnicatable, GenericError.
> 
> ok
> 
> >>   #
> >>   # Since: version
> >>   # Notes: notes, comments can have
> >>   #        - itemized list
> >>   #        - like this
> >>   #
> >>   # Example:
> >>   #
> >>   # -> { "execute": "quit" }
> >>   # <- { "return": {} }
> >>   #
> >>   ##
> >>
> >> That's roughly following the following EBNF grammar:
> >>
> >> api_comment = "##\n" comment "##\n"
> >> comment = freeform_comment | symbol_comment
> >> freeform_comment = { "# " text "\n" | "#\n" }
> >> symbol_comment = "# @" name ":\n" { member | meta | freeform_comment }
> >
> > Rejects non-empty comments where "#" is not followed by space.  Such
> > usage is accepted outside doc comments.  Hmm.
> >
> >> member = "# @" name ':' [ text ] freeform_comment
> >
> > Are you missing a "\n" before freeform_comment?
> 
> yes
> 
> >> meta = "# " ( "Returns:", "Since:", "Note:", "Notes:", "Example:", "Examples:" ) [ text ] freeform_comment
> >
> > Likewise.
> 
> ok
> 
> >> text = free-text markdown-like, "#optional" for members
> >
> > The grammar is ambiguous: a line "# @foo:\n" can be parsed both as
> > freeform_comment and as symbol_comment.  Since your intent is obvious
> > enough, it can still serve as documentation.  It's not a suitable
> > foundation for parsing, though.  Okay for a commit message.
> >
> >> Thanks to the following json expressions, the documentation is enhanced
> >> with extra information about the type of arguments and return value
> >> expected.
> >
> > I guess you want to say that we enrich the documentation we extract from
> > comments with information from the actual schema.  Correct?
> 
> yes
> 
> > Missing: a brief discussion of deficiencies.  These include:
> >
> > * The generated QMP documentation includes internal types
> >
> >   We use qapi-schema.json both for defining the external QMP interface
> >   and for defining internal types.  qmp-introspect.py carefully
> >   separates the two, to not expose internal types.  qapi2texi.py happily
> >   exposes everything.
> >
> >   Currently, about a fifth of the types in the generated docs are
> >   internal:
> >
> >       AcpiTableOptions
> >       BiosAtaTranslation
> >       BlockDeviceMapEntry
> >       COLOMessage
> >       COLOMode
> >       DummyForceArrays
> >       FailoverStatus
> >       FloppyDriveType
> >       ImageCheck
> >       LostTickPolicy
> >       MapEntry
> >       MigrationParameter
> >       NetClientDriver
> >       NetFilterDirection
> >       NetLegacy
> >       NetLegacyNicOptions
> >       NetLegacyOptions
> >       NetLegacyOptionsKind
> >       Netdev
> >       NetdevBridgeOptions
> >       NetdevDumpOptions
> >       NetdevHubPortOptions
> >       NetdevL2TPv3Options
> >       NetdevNetmapOptions
> >       NetdevNoneOptions
> >       NetdevSocketOptions
> >       NetdevTapOptions
> >       NetdevUserOptions
> >       NetdevVdeOptions
> >       NetdevVhostUserOptions
> >       NumaNodeOptions
> >       NumaOptions
> >       NumaOptionsKind
> >       OnOffAuto
> >       OnOffSplit
> >       PreallocMode
> >       QCryptoBlockCreateOptions
> >       QCryptoBlockCreateOptionsLUKS
> >       QCryptoBlockFormat
> >       QCryptoBlockInfo
> >       QCryptoBlockInfoBase
> >       QCryptoBlockInfoQCow
> >       QCryptoBlockOpenOptions
> >       QCryptoBlockOptionsBase
> >       QCryptoBlockOptionsLUKS
> >       QCryptoBlockOptionsQCow
> >       QCryptoSecretFormat
> >       QCryptoTLSCredsEndpoint
> >       QapiErrorClass
> >       ReplayMode
> >       X86CPUFeatureWordInfo
> >       X86CPURegister32
> >
> >   Generating documentation for internal types might be useful, but
> >   letting them pollute QMP interface documentation isn't.  Needs fixing
> >   before we release.  Until then, needs a FIXME comment in qapi2texi.py.
> >
> > * Union support is lacking
> >
> >   The doc string language is adequate for documenting commands, events,
> >   and non-union types.  It doesn't really handle union variants.  Hardly
> >   surprising, as you fitted the language do existing practice, and
> >   existing (mal-)practice is neglecting to document union variant
> >   members.
> >
> > * Documentation is lacking
> >
> >   See review of qapi-code-gen.txt below.
> >
> > * Doc comment error message positions are imprecise
> >
> >   They always point to the beginning of the comment.
> >
> > * Probably more
> >
> >   We should update this with noteworthy findings during review.  I
> >   tried, but I suspect I missed some.
> 
> ok
> 
> >> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> > [Lengthy diffstat snipped...]
> >>
> >> diff --git a/tests/Makefile.include b/tests/Makefile.include
> >> index e98d3b6..f16764c 100644
> >> --- a/tests/Makefile.include
> >> +++ b/tests/Makefile.include
> >> @@ -350,6 +350,21 @@ qapi-schema += base-cycle-direct.json
> >>  qapi-schema += base-cycle-indirect.json
> >>  qapi-schema += command-int.json
> >>  qapi-schema += comments.json
> >> +qapi-schema += doc-bad-args.json
> >> +qapi-schema += doc-bad-symbol.json
> >> +qapi-schema += doc-duplicated-arg.json
> >> +qapi-schema += doc-duplicated-return.json
> >> +qapi-schema += doc-duplicated-since.json
> >> +qapi-schema += doc-empty-arg.json
> >> +qapi-schema += doc-empty-section.json
> >> +qapi-schema += doc-empty-symbol.json
> >> +qapi-schema += doc-invalid-end.json
> >> +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-expr.json
> >> +qapi-schema += doc-missing-space.json
> >>  qapi-schema += double-data.json
> >>  qapi-schema += double-type.json
> >>  qapi-schema += duplicate-key.json
> >> @@ -443,6 +458,8 @@ qapi-schema += union-optional-branch.json
> >>  qapi-schema += union-unknown.json
> >>  qapi-schema += unknown-escape.json
> >>  qapi-schema += unknown-expr-key.json
> >> +
> >> +
> >>  check-qapi-schema-y := $(addprefix tests/qapi-schema/, $(qapi-schema))
> >>  
> >>  GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h \
> >> diff --git a/scripts/qapi.py b/scripts/qapi.py
> >> index 4d1b0e4..1b456b4 100644
> >> --- a/scripts/qapi.py
> >> +++ b/scripts/qapi.py
> >> @@ -122,6 +122,109 @@ class QAPILineError(Exception):
> >>              "%s:%d: %s" % (self.info['file'], self.info['line'], self.msg)
> >>  
> >>  
> >> +class QAPIDoc(object):
> >> +    class Section(object):
> >> +        def __init__(self, name=""):
> >
> > name=None feels more natural than "" for an absent optional name.
> 
> ok
> 
> >> +            # optional section name (argument/member or section name)
> >> +            self.name = name
> >> +            # the list of strings for this section
> >
> > Would "list of lines" be more accurate, or less?
> 
> more accurate, ok
> 
> >> +            self.content = []
> >> +
> >> +        def append(self, line):
> >> +            self.content.append(line)
> >> +
> >> +        def __repr__(self):
> >> +            return "\n".join(self.content).strip()
> >> +
> >> +    class ArgSection(Section):
> >> +        pass
> >
> > Begs the question what makes ArgSection differ from Section.  I think
> > it's the semantics of self.name: an ArgSection's name is the parameter
> > name, a non-ArgSection can either be a "meta" section (name is one of
> > "Returns"", "Since", "Note", "Notes", "Example", "Examples") or an
> > anonymous section (name is "").
> 
> yes, and the section type is checked in  _append_freeform()
> 
> >> +
> >> +    def __init__(self, parser):
> >> +        self.parser = parser
> >> +        self.symbol = None
> >> +        self.body = QAPIDoc.Section()
> >> +        # a dict {'arg': ArgSection, ...}
> >
> > Works.  Alternatevely:
> >
> >            # dict mapping parameter name to ArgSection
> 
> ok
> 
> >> +        self.args = OrderedDict()
> >> +        # a list of Section
> >> +        self.meta = []
> >> +        # the current section
> >> +        self.section = self.body
> >> +        # associated expression and info (set by expression parser)
> >
> > "will be set by expression parser", or "to be set by expression parser"?
> 
> ok
> 
> >> +        self.expr = None
> >> +        self.info = None
> >
> > Clearer now.  The one remark I still have is on the use of "meta" isn't
> > obvious here.  I think a comment further up explaining the different
> > kinds of sections would help.
> >
> > To be honest, I don't get what's "meta" about the "meta" sections :)
> 
> Let's drop that distinction, it isn't necessary anymore
> 
> >> +
> >> +    def has_meta(self, name):
> >> +        """Returns True if the doc has a meta section 'name'"""
> >
> > PEP257[1] demands imperative mood, and punctuation:
> >
> >            """Return True if the doc has a meta section 'name'."""
> >
> > Unfortunately, there seems to be no established convention for marking
> > parameter names.  You put this one in single quotes, which works here,
> > but could be confusing in other cases.  I'd sidestep it:
> >
> >            """Return True if we have a meta section with this name."""
> >
> > or
> >
> >            """Does a meta section with this name exist?"""
> 
> ok
> 
> >> +        for i in self.meta:
> >> +            if i.name == name:
> >> +                return True
> >> +        return False
> >> +
> >> +    def append(self, line):
> >> +        """Adds a # comment line, to be parsed and added to current section"""
> >
> > Imperative mood:
> >
> >     """Add a comment line, to be parsed and added to the current section."""
> >
> > However, we're not always adding to the current section, we can also
> > start a new one.  The following avoids suggesting anything about the
> > current section:
> >
> >     """Parse a comment line and add it to the documentation."""
> >
> > When the function name is a verb, and its doc string starts with a
> > different verb, the name might be suboptimal.  Use your judgement.
> >
> > If this one-liner is too terse, we can try a multi-line doc string:
> >     
> >     """Parse a comment line and add it to the documentation.
> >
> >     TODO should we tell more about how we parse?
> >
> >     Args:
> >         line: the comment starting with '#', with the newline stripped.
> >
> >     Raises:
> >         QAPISchemaError: TODO explain error conditions
> >     """
> >
> > Format stolen from the Google Python Style Guide[2], because PEP257 is
> > of not much help there.
> >
> > Once you start with such elaborate doc strings, pressure will likely
> > mount to do the same elsewhere.  Could be a distraction right now.  Use
> > your judgement.
> 
> ok, thanks
> 
> >> +        line = line[1:]
> >> +        if not line:
> >> +            self._append_freeform(line)
> >> +            return
> >> +
> >> +        if line[0] != ' ':
> >> +            raise QAPISchemaError(self.parser, "missing space after #")
> >> +        line = line[1:]
> >
> > QAPISchemaError takes the error position from its QAPISchemaParser
> > argument.  In other words, the error position depends on the state of
> > the parser when it calls this function.  Turns out the position is the
> > beginning of the comment.  Servicable here.  Action at a distance,
> > though.
> >
> > Less strict:
> >
> >            # strip leading # and space
> >            line = line[1:]
> >            if line[0] == ' ':
> >                line = line[1:]
> >
> > Also avoids special-casing empty comments.
> 
> That would raise "IndexError: string index out of range" on empty comment
> lines ("#")

Fixable:
               if line.startswith(' '):

> Also I'd like to keep the error in case a space is missing (it caught some
> already).

On the one hand, I share your distaste for lack of space between # and
comment text.  On the other hand, I dislike enforcing spacing
inconsistently: enforce in doc comments, but not in other comments.

> > Perhaps we should treat all leading whitespace the same (I'm not sure):
> >
> >            # strip leading # and whitespace
> >            line = line[1:]
> >            line = line.lstrip()
> 
> That would break indentation in Example sections

True; scratch the idea.

> >> +
> >> +        if self.symbol:
> >> +            self._append_symbol_line(line)
> >> +        elif (not self.body.content and
> >> +              line.startswith("@") and line.endswith(":")):
> >> +            self.symbol = line[1:-1]
> >> +            if not self.symbol:
> >> +                raise QAPISchemaError(self.parser, "Invalid symbol")
> >
> > Doesn't recognize the symbol when there's anything other than a single
> > space between # and @.  Pathological case: '#\t@foo:' starting in column
> > 6 looks exactly like '# @foo:', but doesn't work.  Fortunately, your
> > code rejects the tab there.  Assigning meaning to (leading) whitespace
> > may make sense, but it must be documented clearly.
> 
> I think it's simpler to only accept a simple space (# + space + @ + symbol
> + :), it will also lead to more uniform doc.

Simpler is always an argument worth considering, but simpler isn't
always better.  Experience with other languages has taught me to be
carefule when making the kind or amount of whitespace significant.

Kind of whitespace is not a usability issue with your proposal, because
your patch rejects anything but space.

Amount of whitespace is, because getting it wrong turns a symbol comment
block into a free-form comment block, or a parameter section into
free-form lines belonging to whatever section precedes it.

As long as we reject definitions without a symbol comment, errors of the
former kind will still be caught, although the error message could be
confusing.

As is, we don't reject definitions whose symbol comment fails to
document all parameters.  Errors of the latter kind will therefore go
undetected.  Trap for the unwary.  See also "@param2 is True" below.

If we can't find a solution without such usability issues now, then I'm
willing to settle for documentation warning unwary users, or even for a
FIXME comment.

> > Doesn't recognize the symbol when there's crap after ":".  Confusing
> > when it's invisible crap, i.e. trailing whitespace.  In general, it's
> > best to parse left to right, like this:
> >
> >            elif not self.body.content and line.startswith('@'):
> >                match = valid_name.match(line[1:]);
> >                if not match:
> >                    raise QAPISchemaError(self.parser, 'Invalid name')
> >                if line.startswith(':', match.end(0)):
> >                    raise QAPISchemaError(self.parser, 'Expected ":")
> 
> Easier is simply to check :
> if not line.endswith(":"):
>                 raise QAPIParseError(self.parser, "Line should end with :")
> 
> test added

There's a reason we have a mountain of theory on parsing, built in
decades of practice.  What starts "easier" than that generally ends up
rewritten from scratch.  Anyway, let's continue.

> > Here, the error position is clearly suboptimal: it's the beginning of
> > the comment instead of where the actual error is, namely the beginning /
> > the end of the name.  More of the same below.  The root cause is
> > stacking parsers.  I'm not asking you to do anything about it at this
> > time.
> >
> > Note my use of "name" rather than "symbol" in the error message.
> > qapi-code-gen.txt talks about names, not symbols.  Consistent use of
> > terminology matters.
> 
> Agree, isn't "symbol" more appropriate for documentation though?

Possibly.  But if it's an improvement for generated documentation, it
surely is an improvement for hand-written documentation such as
qapi-code-gen.txt and the comments in qapi*.py, too.  And the Python
identifiers.  I have to admit that cools my enthusiasm for making the
improvement right now :)

> >> +        else:
> >> +            self._append_freeform(line)
> >> +
> >> +    def _append_symbol_line(self, line):
> >> +        name = line.split(' ', 1)[0]
> >> +
> >> +        if name.startswith("@") and name.endswith(":"):
> >> +            line = line[len(name)+1:]
> >> +            self._start_args_section(name[1:-1])
> >
> > Similar issue.  Left to right:
> >
> >            if re.match(r'\s*@', line):
> >                match = valid_name.match(line[1:]);
> >                if not match:
> >                    raise QAPISchemaError(self.parser, 'Invalid parameter name')
> >                line = line[match.end(0):]
> >                if line.startswith(':'):
> >                    raise QAPISchemaError(self.parser, 'Expected ":")
> >                line = line[1:]
> >                self._start_args_section(match.group(0))
> >
> > Even better would be factoring out the common code to parse '@foo:'.
> 
> It's a valid case to have a section freeform comment starting with @foo, ex:
> 
> @param: do this and than if
>                @param2 is True

Good point, but it's a language issue, not a parsing issue.

The language issue is that we need to recognize a line "# @param: ..."
as the start of a parameter section, while still permitting free-form
lines like "# @param2 is True".

This is a pretty convincing argument for requiring the ':'.  It can also
be used as an argument for requiring exactly one space between # and @.

How the language is parsed is completely orthogonal.

>  I don't think it's worth to factor out the code to parse @foo vs @foo:, yet
> 
> >> +        elif name in ("Returns:", "Since:",
> >> +                      # those are often singular or plural
> >> +                      "Note:", "Notes:",
> >> +                      "Example:", "Examples:"):
> >> +            line = line[len(name)+1:]
> >> +            self._start_meta_section(name[:-1])
> >
> > Since we're re.match()ing already, here's how do to it that way:
> 
> I don't follow your suggestion for the reason above,so I still have the
> first word 'name'
> 
> >            else
> >                match = re.match(r'\s*(Returns|Since|Notes?|Examples?):', line)
> >                if match:
> >                    line = line[match.end(0):]
> >                    self._start_meta_section(match.group(1))
> 
> not really much nicer to me (especially because no match yet), I'll keep
> the current code for now
> 
> >> +
> >> +        self._append_freeform(line)
> >> +
> >> +    def _start_args_section(self, name):
> >> +        if not name:
> >> +            raise QAPISchemaError(self.parser, "Invalid argument name")
> >
> > parameter name
> 
> ok
> 
> >> +        if name in self.args:
> >> +            raise QAPISchemaError(self.parser, "'%s' arg duplicated" % name)
> >
> > Duplicate parameter name
> 
> ok
> 
> >> +        self.section = QAPIDoc.ArgSection(name)
> >> +        self.args[name] = self.section
> >> +
> >> +    def _start_meta_section(self, name):
> >> +        if name in ("Returns", "Since") and self.has_meta(name):
> >> +            raise QAPISchemaError(self.parser,
> >> +                                  "Duplicated '%s' section" % name)
> >> +        self.section = QAPIDoc.Section(name)
> >> +        self.meta.append(self.section)
> >> +
> >> +    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] \
> >> +           and line and not line[0].isspace():
> >
> > PEP8[3] prefers parenthesises over backslash:
> >
> >            if (in_arg and self.section.content and not self.section.content[-1]
> >                and line and not line[0].isspace()):
> 
> yes, thanks
> 
> >> +            # an empty line followed by a non-indented
> >> +            # comment ends the argument section
> >> +            self.section = self.body
> >> +            self._append_freeform(line)
> >> +            return
> >
> > Switching back to the self.body section like this reorders the
> > documentation text.  I still think this is a terrible idea.  A dumb
> > script is exceedingly unlikely to improve human-written doc comment text
> > by reordering.  In the rare case it does, the doc comment source should
> > be reordered.
> >
> > Here's an example where the doc generator happily creates unintelligible
> > garbage if I format CpuDefinitionInfo's doc comment in a slightly off
> > way:
> >
> >     ##
> >     # @CpuDefinitionInfo:
> >     #
> >     # Virtual CPU definition.
> >     #
> >     # @name: the name of the CPU definition
> >     # 
> >     # @migration-safe: #optional whether a CPU definition can be safely
> >     # used for migration in combination with a QEMU compatibility
> >     # machine when migrating between different QMU versions and between
> >     # hosts with different sets of (hardware or software)
> >     # capabilities.
> >     # 
> >     # If not provided, information is not available and callers should
> >     # not assume the CPU definition to be migration-safe. (since 2.8)
> >     #
> >     # @static: whether a CPU definition is static and will not change
> >     # depending on QEMU version, machine type, machine options and
> >     # accelerator options.  A static model is always
> >     # migration-safe. (since 2.8)
> >     #
> >     # @unavailable-features: #optional List of properties that prevent
> >     # the CPU model from running in the current host. (since 2.8)
> >     #
> >     # @unavailable-features is a list of QOM property names that
> >     # represent CPU model attributes that prevent the CPU from running.
> >     # If the QOM property is read-only, that means there's no known
> >     # way to make the CPU model run in the current host. Implementations
> >     # that choose not to provide specific information return the
> >     # property name "type".
> >     #
> >     # If the property is read-write, it means that it MAY be possible
> >     # to run the CPU model in the current host if that property is
> >     # changed. Management software can use it as hints to suggest or
> >     # choose an alternative for the user, or just to generate meaningful
> >     # error messages explaining why the CPU model can't be used.
> >     # If @unavailable-features is an empty list, the CPU model is
> >     # runnable using the current host and machine-type.
> >     # If @unavailable-features is not present, runnability
> >     # information for the CPU is not available.
> >     #
> >     # Since: 1.2.0
> >     ##
> >     { 'struct': 'CpuDefinitionInfo',
> >       'data': { 'name': 'str', '*migration-safe': 'bool', 'static': 'bool',
> >                 '*unavailable-features': [ 'str' ] } }
> >
> > To detect the problem, you have to read the generated docs attentively.
> > You know my opinion on our chances for that to happen during
> > development.
> >
> > My point is not that we should support this doc comment format.  My
> > point is that people could conceivably write something like it, and not
> > get caught in patch review.
> >
> > I can see three ways out of this swamp:
> >
> > 1. Let sections continue until another one begins.
> 
> but we have interleaved  sections, and no explicit "body" section tag, ex:
> 
> ##
> # @input-send-event:
> #
> # Send input event(s) to guest.
> #
> # @device: #optional display device to send event(s) to.
> # @head: #optional head to send event(s) to, in case the
> #        display device supports multiple scanouts.
> # @events: List of InputEvent union.
> #
> # Returns: Nothing on success.
> #
> # The @device and @head parameters can be used to send the input event
> # to specific input devices in case (a) multiple input devices of the
> # same kind are added to the virtual machine and (b) you have
> # configured input routing (see docs/multiseat.txt) for those input
> # ....
> 
> > 2. Make text between sections an error.
> >
> > 3. When a section ends, start a new anonymous section.
> 
> It's not clear how to recognize when a section ends and append to comment
> body.
> 
> 2. Make text between sections an error.
> 
> Sound like the best option to me. I'll fix the doc and add a check & test
> for this common pattern though:
> 
> ##
> # @TestStruct:
> #
> # body with @var
> #
> # @integer: foo
> #           blah
> #
> #           bao
> #
> # Let's catch this bad comment.
> ##

Go ahead.

> > Can't say offhand which one will work best.
> >
> >> +        if in_arg or not self.section.name.startswith("Example"):
> >> +            line = line.strip()
> >
> > Stripping whitespace is not "Markdown-like", because intendation carries
> > meaning in Markdown.  Example:
> >
> > * First item in itemized list
> > * Second item
> >     * Sub-item of second item
> >     * Another sub-item
> > * Third item
> >
> > Stripping whitespace destroys the list structure.  If that's what you
> > want, you get to document where your "Markdown-like" markup is unlike
> > Markdown :)
> 
> let's use "comment annotations" instead

Okay.

> > Is there a technical reason for stripping whitespace?
> >
> > See also discussion of space after # above.
> >
> >> +        self.section.append(line)
> >> +
> >> +
> >>  class QAPISchemaParser(object):
> >>  
> >>      def __init__(self, fp, previously_included=[], incl_info=None):
> >> @@ -137,11 +240,18 @@ class QAPISchemaParser(object):
> >>          self.line = 1
> >>          self.line_pos = 0
> >>          self.exprs = []
> >> +        self.docs = []
> >>          self.accept()
> >>  
> >>          while self.tok is not None:
> >>              info = {'file': fname, 'line': self.line,
> >>                      'parent': self.incl_info}
> >> +            if self.tok == '#' and self.val.startswith('##'):
> >
> > How can self.val *not* start with '##' here?
> 
> you are right, unnecessary condition since we break get_expr() only in
> this case now
> 
> >> +                doc = self.get_doc()
> >> +                doc.info = info
> >
> > Let's pass info as argument to get_doc(), so we don't have to dot into
> > doc here.  get_doc() can avoid dotting into doc by passing info to the
> > constructor.
> 
> ok
> 
> >> +                self.docs.append(doc)
> >> +                continue
> >> +
> >>              expr = self.get_expr(False)
> >>              if isinstance(expr, dict) and "include" in expr:
> >>                  if len(expr) != 1:
> >> @@ -160,6 +270,7 @@ class QAPISchemaParser(object):
> >>                          raise QAPILineError(info, "Inclusion loop for %s"
> >>                                              % include)
> >>                      inf = inf['parent']
> >> +
> >>                  # skip multiple include of the same file
> >>                  if incl_abs_fname in previously_included:
> >>                      continue
> >> @@ -171,12 +282,38 @@ class QAPISchemaParser(object):
> >>                  exprs_include = QAPISchemaParser(fobj, previously_included,
> >>                                                   info)
> >>                  self.exprs.extend(exprs_include.exprs)
> >> +                self.docs.extend(exprs_include.docs)
> >>              else:
> >>                  expr_elem = {'expr': expr,
> >>                               'info': info}
> >> +                if self.docs and not self.docs[-1].expr:
> >> +                    self.docs[-1].expr = expr
> >> +                    expr_elem['doc'] = self.docs[-1]
> >> +
> >
> > Attaches the expression to the last doc comment that doesn't already
> > have one.  A bit sloppy, because there could be non-doc comments in
> > between, or the doc comment could be in another file.  It'll do, at
> > least for now.
> 
> I extended the condition to check it attaches the doc from the same file
> 
> >>                  self.exprs.append(expr_elem)
> >>  
> >> -    def accept(self):
> >> +    def get_doc(self):
> >> +        if self.val != '##':
> >> +            raise QAPISchemaError(self, "Junk after '##' at start of "
> >> +                                  "documentation comment")
> >> +
> >> +        doc = QAPIDoc(self)
> >> +        self.accept(False)
> >> +        while self.tok == '#':
> >> +            if self.val.startswith('##'):
> >> +                # End of doc comment
> >> +                if self.val != '##':
> >> +                    raise QAPISchemaError(self, "Junk after '##' at end of "
> >> +                                          "documentation comment")
> >> +                self.accept()
> >> +                return doc
> >> +            else:
> >> +                doc.append(self.val)
> >> +            self.accept(False)
> >> +
> >> +        raise QAPISchemaError(self, "Documentation comment must end with '##'")
> >
> > Let's put this after accept, next to the other get_FOO().
> 
> ok
> 
> >> +
> >> +    def accept(self, skip_comment=True):
> >>          while True:
> >>              self.tok = self.src[self.cursor]
> >>              self.pos = self.cursor
> >> @@ -184,7 +321,13 @@ class QAPISchemaParser(object):
> >>              self.val = None
> >>  
> >>              if self.tok == '#':
> >> +                if self.src[self.cursor] == '#':
> >> +                    # Start of doc comment
> >> +                    skip_comment = False
> >>                  self.cursor = self.src.find('\n', self.cursor)
> >> +                if not skip_comment:
> >> +                    self.val = self.src[self.pos:self.cursor]
> >> +                    return
> >>              elif self.tok in "{}:,[]":
> >>                  return
> >>              elif self.tok == "'":
> >
> > Copied from review of v3, so I don't forget:
> >
> > Comment tokens are thrown away as before, except when the parser asks
> > for them by passing skip_comment=False, or when the comment token starts
> > with ##.  The parser asks while parsing a doc comment, in get_doc().
> >
> > This is a backchannel from the parser to the lexer.  I'd rather avoid
> > such lexer hacks, but I guess we can address that on top.
> 
> I don't have a good solution to that

I have an idea which may or may not work.  I can explore it on top.

> > A comment starting with ## inside an expression is now a syntax error.
> > For instance, input
> >
> >     {
> >     ##
> >
> > yields
> >
> >     /dev/stdin:2:1: Expected string or "}"
> >
> > Rather unfriendly error message, but we can fix that on top.
> >
> >> @@ -713,7 +856,7 @@ def check_keys(expr_elem, meta, required, optional=[]):
> >>                                  % (key, meta, name))
> >>  
> >>  
> >> -def check_exprs(exprs):
> >> +def check_exprs(exprs, strict_doc):
> >
> > Note: strict_doc=False unless this is qapi2texi.py.
> 
> For tests reasons: we may want to fix the test instead, or have a flag
> QAPI_CHECK/NOSTRICT_DOC=1 or an option.
> 
> >>      global all_names
> >>  
> >>      # Learn the types and check for valid expression keys
> >> @@ -722,6 +865,11 @@ def check_exprs(exprs):
> >>      for expr_elem in exprs:
> >>          expr = expr_elem['expr']
> >>          info = expr_elem['info']
> >> +
> >> +        if strict_doc and 'doc' not in expr_elem:
> >> +            raise QAPILineError(info,
> >> +                                "Expression missing documentation comment")
> >> +
> >
> > Why should we supress this error in programs other than qapi2texi.py?
> 
> because the tests don't have comments

Good point :)

Can we come up with a brief comment explaining this?

> > Can't see a test for this one.
> 
> because tests aren't strict :)
> 
> I guess you'll tell me to fix the tests instead, so I did that in the next
> series.
> 
> >>          if 'enum' in expr:
> >>              check_keys(expr_elem, 'enum', ['data'], ['prefix'])
> >>              add_enum(expr['enum'], info, expr['data'])
> >> @@ -780,6 +928,63 @@ def check_exprs(exprs):
> >>      return exprs
> >>  
> >>  
> >> +def check_simple_doc(doc):
> >
> > You call this "free-form" elsewhere.  Pick one name and stick to it.
> > I think free-form is more descriptive than simple.
> 
> ok
> 
> >> +    if doc.symbol:
> >> +        raise QAPILineError(doc.info,
> >> +                            "'%s' documention is not followed by the definition"
> >> +                            % doc.symbol)
> >
> > "Documentation for %s is not ..."
> 
> ok
> 
> >> +
> >> +    body = str(doc.body)
> >> +    if re.search(r'@\S+:', body, re.MULTILINE):
> >> +        raise QAPILineError(doc.info,
> >> +                            "Document body cannot contain @NAME: sections")
> >> +
> >> +
> >> +def check_expr_doc(doc, expr, info):
> >
> > You call this "symbol" elsewhere.  I think "definition" would be better
> > than either.
> 
> ok
> 
> >> +    for i in ('enum', 'union', 'alternate', 'struct', 'command', 'event'):
> >> +        if i in expr:
> >> +            meta = i
> >> +            break
> >> +
> >> +    name = expr[meta]
> >> +    if doc.symbol != name:
> >> +        raise QAPILineError(info, "Definition of '%s' follows documentation"
> >> +                            " for '%s'" % (name, doc.symbol))
> >> +    if doc.has_meta('Returns') and 'command' not in expr:
> >> +        raise QAPILineError(info, "Invalid return documentation")
> >
> > Suggest something like "'Returns:' is only valid for commands".
> >
> > We accept 'Returns:' even when the command doesn't return anything,
> > because we currently use it to document errors, too.  Can't say I like
> > that, but it's out of scope here.
> 
> ok
> 
> >> +
> >> +    doc_args = set(doc.args.keys())
> >> +    if meta == 'union':
> >> +        data = expr.get('base', [])
> >> +    else:
> >> +        data = expr.get('data', [])
> >> +    if isinstance(data, dict):
> >> +        data = data.keys()
> >> +    args = set([name.strip('*') for name in data])
> >
> > Not fixed since v3:
> >
> > * Flat union where 'base' is a string, e.g. union UserDefFlatUnion in
> >   qapi-schema-test.json has base 'UserDefUnionBase', args is set(['a',
> >   'B', 'e', 'D', 'f', 'i', 'o', 'n', 's', 'r', 'U'])
> >
> > * Command where 'data' is a string, e.g. user_def_cmd0 in
> >   qapi-schema-test.json has data 'Empty2', args is set(['E', 'm', 'p',
> >   '2', 't', 'y'])
> >
> > * Event where 'data' is a string, no test case handy (hole in test
> >   coverage)
> 
> ok, I changed it that way, that fixes it:
> +    if isinstance(data, list):
> +        args = set([name.strip('*') for name in data])
> +    else:
> +        args = set()

Uh, sure this does the right thing when data is a dict?

> >> +    if meta == 'alternate' or \
> >> +       (meta == 'union' and not expr.get('discriminator')):
> >> +        args.add('type')
> >
> > As explained in review of v3, this is only a subset of the real set of
> > members.  Computing the exact set is impractical when working with the
> > abstract syntax tree.  I believe we'll eventually have to rewrite this
> > code to work with the QAPISchemaEntity instead.
> 
> I don't think we want to list all the members as this would lead to
> duplicated documentation. Instead it should document only the members of
> the expr being defined.

You're sticking to established practice, which makes lots of sense.
However, established practice is a lot more clear for simple cases than
for things like members inherited from base types, variant members and
such.  At some point, we'll have to figure out how we want not so simple
cases documented.  Until then, we can't really decide how to check
documentation completeness.

>                         In which case, it looks like this check is good
> enough, no?

For now, yes.  Later on, maybe.

Let's document the limitation in a comment and the commit message, and
move on.

> >> +    if not doc_args.issubset(args):
> >> +        raise QAPILineError(info, "Members documentation is not a subset of"
> >> +                            " API %r > %r" % (list(doc_args), list(args)))
> >> +
> >> +
> >> +def check_docs(docs):
> >> +    for doc in docs:
> >> +        for section in doc.args.values() + doc.meta:
> >> +            content = str(section)
> >> +            if not content or content.isspace():
> >> +                raise QAPILineError(doc.info,
> >> +                                    "Empty doc section '%s'" % section.name)
> >> +
> >> +        if not doc.expr:
> >> +            check_simple_doc(doc)
> >> +        else:
> >> +            check_expr_doc(doc, doc.expr, doc.info)
> >> +
> >> +    return docs
> >> +
> >> +
> >>  #
> >>  # Schema compiler frontend
> >>  #
> >> @@ -1249,9 +1454,11 @@ class QAPISchemaEvent(QAPISchemaEntity):
> >>  
> >>  
> >>  class QAPISchema(object):
> >> -    def __init__(self, fname):
> >> +    def __init__(self, fname, strict_doc=False):
> >>          try:
> >> -            self.exprs = check_exprs(QAPISchemaParser(open(fname, "r")).exprs)
> >> +            parser = QAPISchemaParser(open(fname, "r"))
> >> +            self.exprs = check_exprs(parser.exprs, strict_doc)
> >> +            self.docs = check_docs(parser.docs)
> >>              self._entity_dict = {}
> >>              self._predefining = True
> >>              self._def_predefineds()
> >> diff --git a/scripts/qapi2texi.py b/scripts/qapi2texi.py
> >> new file mode 100755
> >> index 0000000..0cec43a
> >> --- /dev/null
> >> +++ b/scripts/qapi2texi.py
> >
> > Still only skimming this one.
> >
> >> @@ -0,0 +1,331 @@
> >> +#!/usr/bin/env python
> >> +# QAPI texi generator
> >> +#
> >> +# This work is licensed under the terms of the GNU LGPL, version 2+.
> >> +# See the COPYING file in the top-level directory.
> >> +"""This script produces the documentation of a qapi schema in texinfo format"""
> >> +import re
> >> +import sys
> >> +
> >> +import qapi
> >> +
> >> +COMMAND_FMT = """
> >> +@deftypefn {type} {{{ret}}} {name} @
> >> +{{{args}}}
> >> +
> >> +{body}
> >> +
> >> +@end deftypefn
> >> +
> >> +""".format
> >> +
> >> +ENUM_FMT = """
> >> +@deftp Enum {name}
> >> +
> >> +{body}
> >> +
> >> +@end deftp
> >> +
> >> +""".format
> >> +
> >> +STRUCT_FMT = """
> >> +@deftp {{{type}}} {name} @
> >> +{{{attrs}}}
> >> +
> >> +{body}
> >> +
> >> +@end deftp
> >> +
> >> +""".format
> >> +
> >> +EXAMPLE_FMT = """@example
> >> +{code}
> >> +@end example
> >> +""".format
> >> +
> >> +
> >> +def subst_strong(doc):
> >> +    """Replaces *foo* by @strong{foo}"""
> >> +    return re.sub(r'\*([^_\n]+)\*', r'@emph{\1}', doc)
> >> +
> >> +
> >> +def subst_emph(doc):
> >> +    """Replaces _foo_ by @emph{foo}"""
> >> +    return re.sub(r'\s_([^_\n]+)_\s', r' @emph{\1} ', doc)
> >> +
> >> +
> >> +def subst_vars(doc):
> >> +    """Replaces @var by @code{var}"""
> >> +    return re.sub(r'@([\w-]+)', r'@code{\1}', doc)
> >> +
> >> +
> >> +def subst_braces(doc):
> >> +    """Replaces {} with @{ @}"""
> >> +    return doc.replace("{", "@{").replace("}", "@}")
> >> +
> >> +
> >> +def texi_example(doc):
> >> +    """Format @example"""
> >> +    doc = subst_braces(doc).strip('\n')
> >> +    return EXAMPLE_FMT(code=doc)
> >> +
> >> +
> >> +def texi_comment(doc):
> >> +    """
> >> +    Format a comment
> >> +
> >> +    Lines starting with:
> >> +    - |: generates an @example
> >> +    - =: generates @section
> >> +    - ==: generates @subsection
> >> +    - 1. or 1): generates an @enumerate @item
> >> +    - o/*/-: generates an @itemize list
> >> +    """
> >> +    lines = []
> >> +    doc = subst_braces(doc)
> >> +    doc = subst_vars(doc)
> >> +    doc = subst_emph(doc)
> >> +    doc = subst_strong(doc)
> >> +    inlist = ""
> >> +    lastempty = False
> >> +    for line in doc.split('\n'):
> >> +        empty = line == ""
> >> +
> >> +        if line.startswith("| "):
> >> +            line = EXAMPLE_FMT(code=line[2:])
> >> +        elif line.startswith("= "):
> >> +            line = "@section " + line[2:]
> >> +        elif line.startswith("== "):
> >> +            line = "@subsection " + line[3:]
> >> +        elif re.match("^([0-9]*[.)]) ", line):
> >> +            if not inlist:
> >> +                lines.append("@enumerate")
> >> +                inlist = "enumerate"
> >> +            line = line[line.find(" ")+1:]
> >> +            lines.append("@item")
> >> +        elif re.match("^[o*-] ", line):
> >> +            if not inlist:
> >> +                lines.append("@itemize %s" % {'o': "@bullet",
> >> +                                              '*': "@minus",
> >> +                                              '-': ""}[line[0]])
> >> +                inlist = "itemize"
> >> +            lines.append("@item")
> >> +            line = line[2:]
> >> +        elif lastempty and inlist:
> >> +            lines.append("@end %s\n" % inlist)
> >> +            inlist = ""
> >> +
> >> +        lastempty = empty
> >> +        lines.append(line)
> >> +
> >> +    if inlist:
> >> +        lines.append("@end %s\n" % inlist)
> >> +    return "\n".join(lines)
> >> +
> >> +
> >> +def texi_args(expr, key="data"):
> >> +    """
> >> +    Format the functions/structure/events.. arguments/members
> >> +    """
> >> +    if key not in expr:
> >> +        return ""
> >> +
> >> +    args = expr[key]
> >> +    arg_list = []
> >> +    if isinstance(args, str):
> >> +        arg_list.append(args)
> >> +    else:
> >> +        for name, typ in args.iteritems():
> >> +            # optional arg
> >> +            if name.startswith("*"):
> >> +                name = name[1:]
> >> +                arg_list.append("['%s': @var{%s}]" % (name, typ))
> >> +            # regular arg
> >> +            else:
> >> +                arg_list.append("'%s': @var{%s}" % (name, typ))
> >
> > Inappropriate use of @var.  @var is for metasyntactic variables,
> > i.e. something that stands for another piece of text.  typ isn't, it's
> > the name of a specific QAPI type.  I think you should use @code.
> 
> yes
> 
> > This is the reason why the type names in qemu-qmp-ref.txt are often
> > mangled, e.g.
> >
> >  -- Struct: VersionInfo { 'qemu': VERSIONTRIPLE, 'package': STR }
> 
> Right, we have format issue here. If we change it for @code, we get
> additional quotes in the text format. The simpler is to use no format or
> @t{}

Pick something you like.

> >> +
> >> +    return ", ".join(arg_list)
> >> +
> >> +
> >> +def texi_body(doc):
> >> +    """
> >> +    Format the body of a symbol documentation:
> >> +    - a table of arguments
> >> +    - followed by "Returns/Notes/Since/Example" sections
> >> +    """
> >> +    def _section_order(section):
> >> +        return {"Returns": 0,
> >> +                "Note": 1,
> >> +                "Notes": 1,
> >> +                "Since": 2,
> >> +                "Example": 3,
> >> +                "Examples": 3}[section]
> >> +
> >> +    body = "@table @asis\n"
> >> +    for arg, section in doc.args.iteritems():
> >> +        desc = str(section)
> >> +        opt = ''
> >> +        if desc.startswith("#optional"):
> >> +            desc = desc[10:]
> >> +            opt = ' *'
> >> +        elif desc.endswith("#optional"):
> >> +            desc = desc[:-10]
> >> +            opt = ' *'
> >> +        body += "@item @code{'%s'}%s\n%s\n" % (arg, opt, texi_comment(desc))
> >> +    body += "@end table\n"
> >> +    body += texi_comment(str(doc.body))
> >> +
> >> +    meta = sorted(doc.meta, key=lambda i: _section_order(i.name))
> >> +    for section in meta:
> >> +        name, doc = (section.name, str(section))
> >> +        func = texi_comment
> >> +        if name.startswith("Example"):
> >> +            func = texi_example
> >> +
> >> +        body += "\n@quotation %s\n%s\n@end quotation" % \
> >> +                (name, func(doc))
> >> +    return body
> >> +
> >> +
> >> +def texi_alternate(expr, doc):
> >> +    """
> >> +    Format an alternate to texi
> >> +    """
> >> +    args = texi_args(expr)
> >> +    body = texi_body(doc)
> >> +    return STRUCT_FMT(type="Alternate",
> >> +                      name=doc.symbol,
> >> +                      attrs="[ " + args + " ]",
> >> +                      body=body)
> >> +
> >> +
> >> +def texi_union(expr, doc):
> >> +    """
> >> +    Format an union to texi
> >
> > I think it's "a union".
> 
> ok
> 
> >> +    """
> >> +    attrs = "@{ " + texi_args(expr, "base") + " @}"
> >> +    discriminator = expr.get("discriminator")
> >> +    if not discriminator:
> >> +        union = "Flat Union"
> >> +        discriminator = "type"
> >> +    else:
> >> +        union = "Union"
> >
> > Condition is backwards.
> 
> fixed
> 
> >> +    attrs += " + '%s' = [ " % discriminator
> >> +    attrs += texi_args(expr, "data") + " ]"
> >> +    body = texi_body(doc)
> >> +
> >> +    return STRUCT_FMT(type=union,
> >> +                      name=doc.symbol,
> >> +                      attrs=attrs,
> >> +                      body=body)
> >
> > You're inventing syntax here.  Example output:
> >
> >  -- Union: QCryptoBlockOpenOptions { QCryptoBlockOptionsBase } +
> >           'format' = [ 'qcow': QCRYPTOBLOCKOPTIONSQCOW, 'luks':
> >           QCRYPTOBLOCKOPTIONSLUKS ]
> >
> >      The options that are available for all encryption formats when
> >      opening an existing volume
> >           Since: 2.6
> >
> >  -- Flat Union: ImageInfoSpecific { } + 'type' = [ 'qcow2':
> >           IMAGEINFOSPECIFICQCOW2, 'vmdk': IMAGEINFOSPECIFICVMDK, 'luks':
> >           QCRYPTOBLOCKINFOLUKS ]
> >
> >      A discriminated record of image format specific information
> >      structures.
> >           Since: 1.7
> >
> > Note that QCryptoBlockOpenOptions is actually a flat union, and
> > ImageInfoSpecific a simple union.  As I said, the condition is
> > backwards.
> >
> > The meaning of the attrs part is unobvious.  Familiarity with schema
> > syntax doesn't really help.
> >
> > Could we simply use schema syntax here?
> 
> Union: QCryptoBlockOpenOptions {
> 'base': QCryptoBlockOptionsBase,
> 'discriminator': 'format',
> 'data':  { 'qcow': QCryptoBlockOptionsQCow, 'luks':
> QCryptoBlockCreateOptionsLUKS }
> 
> }
> 
> Doesn't look obvious either and pollute the documentation with
> schema-specific parameters.

The schema syntax for flat unions is pretty horrid :)  I hope to improve
it, but there's more urgent fish to fry.

> > If not: whatever format you use, you first need to explain it.
> 
> I am not sure my solution is the best and will remain, but ok let's try to
> document it for now.

Before you pour time into documenting what you have, we should probably
discuss what we need.

> >> +
> >> +
> >> +def texi_enum(expr, doc):
> >> +    """
> >> +    Format an enum to texi
> >> +    """
> >> +    for i in expr['data']:
> >> +        if i not in doc.args:
> >> +            doc.args[i] = ''
> >> +    body = texi_body(doc)
> >> +    return ENUM_FMT(name=doc.symbol,
> >> +                    body=body)
> >> +
> >> +
> >> +def texi_struct(expr, doc):
> >> +    """
> >> +    Format a struct to texi
> >> +    """
> >> +    args = texi_args(expr)
> >> +    body = texi_body(doc)
> >> +    attrs = "@{ " + args + " @}"
> >> +    base = expr.get("base")
> >> +    if base:
> >> +        attrs += " + %s" % base
> >> +    return STRUCT_FMT(type="Struct",
> >> +                      name=doc.symbol,
> >> +                      attrs=attrs,
> >> +                      body=body)
> >
> > More syntax invention.  Example output:
> >
> >  -- Struct: BlockdevOptionsReplication { 'mode': REPLICATIONMODE,
> >           ['top-id': STR] } + BlockdevOptionsGenericFormat
> >
> >      ''mode''
> >           the replication mode
> >      ''top-id'' *
> >           In secondary mode, node name or device ID of the root node who
> >           owns the replication node chain.  Must not be given in primary
> >           mode.
> >      Driver specific block device options for replication
> >           Since: 2.8
> >
> > Meaning of the attrs part is perhaps more guessable here, but it's still
> > guesswork.
> >
> > The meaning of * after ''top-id'' is also unobvious.
> >
> > Note the redundancy between the attrs part and the body: both state
> > member names and optionalness.  The body doesn't state member types and
> > base type.  If we fixed that, we could drop the attrs part and save us
> > the trouble of defining and explaining a syntax for it.
> >
> > Let me take a step back.  This document is about the QMP wire format.
> > There are no such things as simple and flat unions on the wire, only
> > JSON objects.  QMP introspection duly describes a type's JSON objects,
> > not how it's defined in QAPI.  I think QMP documentation should ideally
> > do the same.
> >
> > QMP introspection uses a common structure for struct, simple and flat
> > union: common members, variants, and if variants, then the common member
> > that serves as tag.  See introspect.json for details.
> >
> > Base types are flattened away.  Appropriate for introspection, but
> > documentation shouldn't do that.
> >
> > I wrote "ideally" because it's probably too big a step.  I'm willing to
> > settle for something less than ideal.
> 
> I don't have clear idea what to do here, so if we can leave that for later,
> that would be nice for me (I am already spending more time than I imagined
> I would on doc stuff)

Me too, if that's any consolation...

Perhaps I can find a bit of time to think so I can propose what we could
do.

> >> +
> >> +
> >> +def texi_command(expr, doc):
> >> +    """
> >> +    Format a command to texi
> >> +    """
> >> +    args = texi_args(expr)
> >> +    ret = expr["returns"] if "returns" in expr else ""
> >> +    body = texi_body(doc)
> >> +    return COMMAND_FMT(type="Command",
> >> +                       name=doc.symbol,
> >> +                       ret=ret,
> >> +                       args="(" + args + ")",
> >> +                       body=body)
> >> +
> >> +
> >> +def texi_event(expr, doc):
> >> +    """
> >> +    Format an event to texi
> >> +    """
> >> +    args = texi_args(expr)
> >> +    body = texi_body(doc)
> >> +    return COMMAND_FMT(type="Event",
> >> +                       name=doc.symbol,
> >> +                       ret="",
> >> +                       args="(" + args + ")",
> >> +                       body=body)
> >> +
> >> +
> >> +def texi_expr(expr, doc):
> >> +    """
> >> +    Format an expr to texi
> >> +    """
> >> +    (kind, _) = expr.items()[0]
> >> +
> >> +    fmt = {"command": texi_command,
> >> +           "struct": texi_struct,
> >> +           "enum": texi_enum,
> >> +           "union": texi_union,
> >> +           "alternate": texi_alternate,
> >> +           "event": texi_event}
> >> +    try:
> >> +        fmt = fmt[kind]
> >> +    except KeyError:
> >> +        raise ValueError("Unknown expression kind '%s'" % kind)
> >
> > The try / except converts one kind of error into another.  What does
> > that buy us?  As far as I can tell, this shouldn't ever happen.
> 
> dropped
> 
> >> +
> >> +    return fmt(expr, doc)
> >> +
> >> +
> >> +def texi(docs):
> >> +    """
> >> +    Convert QAPI schema expressions to texi documentation
> >> +    """
> >> +    res = []
> >> +    for doc in docs:
> >> +        expr = doc.expr
> >> +        if not expr:
> >> +            res.append(texi_body(doc))
> >> +            continue
> >> +        try:
> >> +            doc = texi_expr(expr, doc)
> >> +            res.append(doc)
> >> +        except:
> >> +            print >>sys.stderr, "error at @%s" % doc.info
> >> +            raise
> >> +
> >> +    return '\n'.join(res)
> >> +
> >> +
> >> +def main(argv):
> >> +    """
> >> +    Takes schema argument, prints result to stdout
> >> +    """
> >> +    if len(argv) != 2:
> >> +        print >>sys.stderr, "%s: need exactly 1 argument: SCHEMA" % argv[0]
> >> +        sys.exit(1)
> >> +
> >> +    schema = qapi.QAPISchema(argv[1], strict_doc=True)
> >> +    print texi(schema.docs)
> >> +
> >> +
> >> +if __name__ == "__main__":
> >> +    main(sys.argv)
> >> diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
> >> index 2841c51..8bc963e 100644
> >> --- a/docs/qapi-code-gen.txt
> >> +++ b/docs/qapi-code-gen.txt
> >> @@ -45,16 +45,13 @@ QAPI parser does not).  At present, there is no place where a QAPI
> >>  schema requires the use of JSON numbers or null.
> >>  
> >>  Comments are allowed; anything between an unquoted # and the following
> >> -newline is ignored.  Although there is not yet a documentation
> >> -generator, a form of stylized comments has developed for consistently
> >> -documenting details about an expression and when it was added to the
> >> -schema.  The documentation is delimited between two lines of ##, then
> >> -the first line names the expression, an optional overview is provided,
> >> -then individual documentation about each member of 'data' is provided,
> >> -and finally, a 'Since: x.y.z' tag lists the release that introduced
> >> -the expression.  Optional members are tagged with the phrase
> >> -'#optional', often with their default value; and extensions added
> >> -after the expression was first released are also given a '(since
> >> +newline is ignored.  The documentation is delimited between two lines
> >> +of ##, then the first line names the expression, an optional overview
> >> +is provided, then individual documentation about each member of 'data'
> >> +is provided, and finally, a 'Since: x.y.z' tag lists the release that
> >> +introduced the expression.  Optional members are tagged with the
> >> +phrase '#optional', often with their default value; and extensions
> >> +added after the expression was first released are also given a '(since
> >>  x.y.z)' comment.  For example:
> >>  
> >>      ##
> >> @@ -73,12 +70,49 @@ x.y.z)' comment.  For example:
> >>      #           (Since 2.0)
> >>      #
> >>      # Since: 0.14.0
> >> +    #
> >> +    # Notes: You can also make a list:
> >> +    #        - with items
> >> +    #        - like this
> >> +    #
> >> +    # Example:
> >> +    #
> >> +    # -> { "execute": ... }
> >> +    # <- { "return": ... }
> >> +    #
> >>      ##
> >>      { 'struct': 'BlockStats',
> >>        'data': {'*device': 'str', 'stats': 'BlockDeviceStats',
> >>                 '*parent': 'BlockStats',
> >>                 '*backing': 'BlockStats'} }
> >
> > This example gives an idea of how to document struct types.  But the
> > reader is left guessing how to document other kinds of definitions.  In
> > particular, there's no mention of "Returns", "Note" and "Examples"
> > sections anywhere in this file.  As is, this could do for a tutorial,
> > but this file is a *reference*, not a tutorial.
> >
> > For a reference, we need to be more thorough.  A doc comments section on
> > its own seems advisable.
> >
> > I guess doc comment examples are best added to the schema code examples
> > in sections === Struct types ===, ..., === Events ===.
> >
> > Careful review for completeness is advised.
> 
> So far we did quite fine with the generic example. I am not convinced we
> need to have doc comments for all kinds of types, it looks redundant to me.

Well, so far we didn't require people to write well-formed doc comments.

Without such comments in qapi-code-gen.txt's examples, they become
incomplete.  Pity, because I like to feed them to the QAPI generators;
extracted like this:

    $ sed '1,/\$ cat example-schema.json/d;/^[^ ]/,$d;s/^    //' ../docs/qapi-code-gen.txt >example-schema.json

> >> +It's also possible to create documentation sections, such as:
> >> +
> >> +    ##
> >> +    # = Section
> >> +    # == Subsection
> >> +    #
> >> +    # Some text foo with *strong* and _emphasis_
> >> +    # 1. with a list
> >> +    # 2. like that
> >> +    #
> >> +    # And some code:
> >> +    # | $ echo foo
> >> +    # | -> do this
> >> +    # | <- get that
> >> +    #
> >> +    ##
> >> +
> >> +Text *foo* and _foo_ are for "strong" and "emphasis" styles (they do
> >> +not work over multiple lines). @foo is used to reference a symbol.
> >> +
> >> +Lines starting with the following characters and a space:
> >> +- | are examples
> >> +- = are top section
> >> +- == are subsection
> >> +- X. or X) are enumerations (X is any number)
> >> +- o/*/- are itemized list
> >> +
> >
> > Is this doc markup documentation complete?
> 
> I think so
> 
> > Is it related to any existing text markup language?  Hmm, the commit
> > message calls it "Markdown-like".  Should we mention that here?
> 
> I'll use "annotations" instead in the doc
> 
> > Is all markup valid in all contexts?  For instance, is = Section valid
> > in symbol blocks?  What does | (or any markup for that matter) do within
> > an Example section?
> 
> Example is verbatim
> 
> I improved a bit the doc based on your feedback. I'd suggest we iterate
> with additional patches later, I could use help.

Let's mark places that need improvement with FIXME or TODO comments,
noted in commit messages.

> >>  The schema sets up a series of types, as well as commands and events
> >>  that will use those types.  Forward references are allowed: the parser
> >>  scans in two passes, where the first pass learns all type names, and
> >> diff --git a/tests/qapi-schema/doc-bad-args.err b/tests/qapi-schema/doc-bad-args.err
> >> new file mode 100644
> >> index 0000000..a55e003
> >> --- /dev/null
> >> +++ b/tests/qapi-schema/doc-bad-args.err
> >> @@ -0,0 +1 @@
> >> +tests/qapi-schema/doc-bad-args.json:1: Members documentation is not a subset of API ['a', 'b'] > ['a']
> >> diff --git a/tests/qapi-schema/doc-bad-args.exit b/tests/qapi-schema/doc-bad-args.exit
> >> new file mode 100644
> >> index 0000000..d00491f
> >> --- /dev/null
> >> +++ b/tests/qapi-schema/doc-bad-args.exit
> >> @@ -0,0 +1 @@
> >> +1
> >> diff --git a/tests/qapi-schema/doc-bad-args.json b/tests/qapi-schema/doc-bad-args.json
> >> new file mode 100644
> >> index 0000000..26992ea
> >> --- /dev/null
> >> +++ b/tests/qapi-schema/doc-bad-args.json
> >> @@ -0,0 +1,6 @@
> >> +##
> >> +# @foo:
> >> +# @a: a
> >> +# @b: b
> >> +##
> >> +{ 'command': 'foo', 'data': {'a': 'int'} }
> >
> > The existing tests/qapi-schema/*.json explain what is being tested in a
> > comment.  Perhaps like this:
> >
> >     # Arguments listed in the doc comment must exist in the actual schema
> >
> >     ##
> >     # @foo:
> >     ...
> >
> > Likewise for other tests.
> 
> ok
> 
[...]
> > [1] https://www.python.org/dev/peps/pep-0257/
> > [2] https://google.github.io/styleguide/pyguide.html
> > [3] https://www.python.org/dev/peps/pep-0008/

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v5 13/17] qapi: add qapi2texi script
  2016-12-06 11:50       ` Markus Armbruster
@ 2016-12-06 13:07         ` Marc-André Lureau
  2016-12-07 16:05           ` Markus Armbruster
  0 siblings, 1 reply; 48+ messages in thread
From: Marc-André Lureau @ 2016-12-06 13:07 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel

Hi

On Tue, Dec 6, 2016 at 2:50 PM Markus Armbruster <armbru@redhat.com> wrote:

> I had to resort to diff to find your replies, and massage the text
> manually to produce a readable reply myself.  Please quote the usual
> way.
>
>
I'd have to switch to something else than gmail (which bothers me for
various reasons, let's not discuss the merits of various mail clients
please ;) In general, I don't have problems, but this mail is rather big,
sorry for the inconvenience..

> Markus Armbruster <armbru@redhat.com> writes:
> >
> > > Marc-André Lureau <marcandre.lureau@redhat.com> writes:
> > >
> > >> As the name suggests, the qapi2texi script converts JSON QAPI
> > >> description into a texi file suitable for different target
> > >> formats (info/man/txt/pdf/html...).
> > >>
> > >> It parses the following kind of blocks:
> > >>
> > >> Free-form:
> > >>
> > >>   ##
> > >>   # = Section
> > >>   # == Subsection
> > >>   #
> > >>   # Some text foo with *emphasis*
> > >>   # 1. with a list
> > >>   # 2. like that
> > >>   #
> > >>   # And some code:
> > >>   # | $ echo foo
> > >>   # | -> do this
> > >>   # | <- get that
> > >>   #
> > >>   ##
> > >>
> > >> Symbol:
> > >>
> > >>   ##
> > >>   # @symbol:
> > >>   #
> > >>   # Symbol body ditto ergo sum. Foo bar
> > >>   # baz ding.
> > >>   #
> > >>   # @arg: foo
> > >>   # @arg: #optional foo
> > >
> > > Let's not use @arg twice.
> > >
> > > Terminology: I prefer to use "parameter" for formal parameters, and
> > > "argument" for actual arguments.  This matches how The Python Language
> > > Reference uses the terms.
> > >
> > > What about
> > >
> > >     # @param1: the frob to frobnicate
> > >     # @param2: #optional how hard to frobnicate
> >
> > ok
> >
> > >>   #
> > >>   # Returns: returns bla bla
> > >>   #          Or bla blah
> > >
> > > Repeating "returns" is awkward, and we don't do that in our schemas.
> > >
> > > We need a period before "Or", or spell it "or".
> > >
> > > What about
> > >
> > >     # Returns: the frobnicated frob.
> > >     #          If frob isn't frobnicatable, GenericError.
> >
> > ok
> >
> > >>   #
> > >>   # Since: version
> > >>   # Notes: notes, comments can have
> > >>   #        - itemized list
> > >>   #        - like this
> > >>   #
> > >>   # Example:
> > >>   #
> > >>   # -> { "execute": "quit" }
> > >>   # <- { "return": {} }
> > >>   #
> > >>   ##
> > >>
> > >> That's roughly following the following EBNF grammar:
> > >>
> > >> api_comment = "##\n" comment "##\n"
> > >> comment = freeform_comment | symbol_comment
> > >> freeform_comment = { "# " text "\n" | "#\n" }
> > >> symbol_comment = "# @" name ":\n" { member | meta | freeform_comment }
> > >
> > > Rejects non-empty comments where "#" is not followed by space.  Such
> > > usage is accepted outside doc comments.  Hmm.
> > >
> > >> member = "# @" name ':' [ text ] freeform_comment
> > >
> > > Are you missing a "\n" before freeform_comment?
> >
> > yes
> >
> > >> meta = "# " ( "Returns:", "Since:", "Note:", "Notes:", "Example:",
> "Examples:" ) [ text ] freeform_comment
> > >
> > > Likewise.
> >
> > ok
> >
> > >> text = free-text markdown-like, "#optional" for members
> > >
> > > The grammar is ambiguous: a line "# @foo:\n" can be parsed both as
> > > freeform_comment and as symbol_comment.  Since your intent is obvious
> > > enough, it can still serve as documentation.  It's not a suitable
> > > foundation for parsing, though.  Okay for a commit message.
> > >
> > >> Thanks to the following json expressions, the documentation is
> enhanced
> > >> with extra information about the type of arguments and return value
> > >> expected.
> > >
> > > I guess you want to say that we enrich the documentation we extract
> from
> > > comments with information from the actual schema.  Correct?
> >
> > yes
> >
> > > Missing: a brief discussion of deficiencies.  These include:
> > >
> > > * The generated QMP documentation includes internal types
> > >
> > >   We use qapi-schema.json both for defining the external QMP interface
> > >   and for defining internal types.  qmp-introspect.py carefully
> > >   separates the two, to not expose internal types.  qapi2texi.py
> happily
> > >   exposes everything.
> > >
> > >   Currently, about a fifth of the types in the generated docs are
> > >   internal:
> > >
> > >       AcpiTableOptions
> > >       BiosAtaTranslation
> > >       BlockDeviceMapEntry
> > >       COLOMessage
> > >       COLOMode
> > >       DummyForceArrays
> > >       FailoverStatus
> > >       FloppyDriveType
> > >       ImageCheck
> > >       LostTickPolicy
> > >       MapEntry
> > >       MigrationParameter
> > >       NetClientDriver
> > >       NetFilterDirection
> > >       NetLegacy
> > >       NetLegacyNicOptions
> > >       NetLegacyOptions
> > >       NetLegacyOptionsKind
> > >       Netdev
> > >       NetdevBridgeOptions
> > >       NetdevDumpOptions
> > >       NetdevHubPortOptions
> > >       NetdevL2TPv3Options
> > >       NetdevNetmapOptions
> > >       NetdevNoneOptions
> > >       NetdevSocketOptions
> > >       NetdevTapOptions
> > >       NetdevUserOptions
> > >       NetdevVdeOptions
> > >       NetdevVhostUserOptions
> > >       NumaNodeOptions
> > >       NumaOptions
> > >       NumaOptionsKind
> > >       OnOffAuto
> > >       OnOffSplit
> > >       PreallocMode
> > >       QCryptoBlockCreateOptions
> > >       QCryptoBlockCreateOptionsLUKS
> > >       QCryptoBlockFormat
> > >       QCryptoBlockInfo
> > >       QCryptoBlockInfoBase
> > >       QCryptoBlockInfoQCow
> > >       QCryptoBlockOpenOptions
> > >       QCryptoBlockOptionsBase
> > >       QCryptoBlockOptionsLUKS
> > >       QCryptoBlockOptionsQCow
> > >       QCryptoSecretFormat
> > >       QCryptoTLSCredsEndpoint
> > >       QapiErrorClass
> > >       ReplayMode
> > >       X86CPUFeatureWordInfo
> > >       X86CPURegister32
> > >
> > >   Generating documentation for internal types might be useful, but
> > >   letting them pollute QMP interface documentation isn't.  Needs fixing
> > >   before we release.  Until then, needs a FIXME comment in
> qapi2texi.py.
> > >
> > > * Union support is lacking
> > >
> > >   The doc string language is adequate for documenting commands, events,
> > >   and non-union types.  It doesn't really handle union variants.
> Hardly
> > >   surprising, as you fitted the language do existing practice, and
> > >   existing (mal-)practice is neglecting to document union variant
> > >   members.
> > >
> > > * Documentation is lacking
> > >
> > >   See review of qapi-code-gen.txt below.
> > >
> > > * Doc comment error message positions are imprecise
> > >
> > >   They always point to the beginning of the comment.
> > >
> > > * Probably more
> > >
> > >   We should update this with noteworthy findings during review.  I
> > >   tried, but I suspect I missed some.
> >
> > ok
> >
> > >> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> > > [Lengthy diffstat snipped...]
> > >>
> > >> diff --git a/tests/Makefile.include b/tests/Makefile.include
> > >> index e98d3b6..f16764c 100644
> > >> --- a/tests/Makefile.include
> > >> +++ b/tests/Makefile.include
> > >> @@ -350,6 +350,21 @@ qapi-schema += base-cycle-direct.json
> > >>  qapi-schema += base-cycle-indirect.json
> > >>  qapi-schema += command-int.json
> > >>  qapi-schema += comments.json
> > >> +qapi-schema += doc-bad-args.json
> > >> +qapi-schema += doc-bad-symbol.json
> > >> +qapi-schema += doc-duplicated-arg.json
> > >> +qapi-schema += doc-duplicated-return.json
> > >> +qapi-schema += doc-duplicated-since.json
> > >> +qapi-schema += doc-empty-arg.json
> > >> +qapi-schema += doc-empty-section.json
> > >> +qapi-schema += doc-empty-symbol.json
> > >> +qapi-schema += doc-invalid-end.json
> > >> +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-expr.json
> > >> +qapi-schema += doc-missing-space.json
> > >>  qapi-schema += double-data.json
> > >>  qapi-schema += double-type.json
> > >>  qapi-schema += duplicate-key.json
> > >> @@ -443,6 +458,8 @@ qapi-schema += union-optional-branch.json
> > >>  qapi-schema += union-unknown.json
> > >>  qapi-schema += unknown-escape.json
> > >>  qapi-schema += unknown-expr-key.json
> > >> +
> > >> +
> > >>  check-qapi-schema-y := $(addprefix tests/qapi-schema/,
> $(qapi-schema))
> > >>
> > >>  GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h
> \
> > >> diff --git a/scripts/qapi.py b/scripts/qapi.py
> > >> index 4d1b0e4..1b456b4 100644
> > >> --- a/scripts/qapi.py
> > >> +++ b/scripts/qapi.py
> > >> @@ -122,6 +122,109 @@ class QAPILineError(Exception):
> > >>              "%s:%d: %s" % (self.info['file'], self.info['line'],
> self.msg)
> > >>
> > >>
> > >> +class QAPIDoc(object):
> > >> +    class Section(object):
> > >> +        def __init__(self, name=""):
> > >
> > > name=None feels more natural than "" for an absent optional name.
> >
> > ok
> >
> > >> +            # optional section name (argument/member or section name)
> > >> +            self.name = name
> > >> +            # the list of strings for this section
> > >
> > > Would "list of lines" be more accurate, or less?
> >
> > more accurate, ok
> >
> > >> +            self.content = []
> > >> +
> > >> +        def append(self, line):
> > >> +            self.content.append(line)
> > >> +
> > >> +        def __repr__(self):
> > >> +            return "\n".join(self.content).strip()
> > >> +
> > >> +    class ArgSection(Section):
> > >> +        pass
> > >
> > > Begs the question what makes ArgSection differ from Section.  I think
> > > it's the semantics of self.name: an ArgSection's name is the parameter
> > > name, a non-ArgSection can either be a "meta" section (name is one of
> > > "Returns"", "Since", "Note", "Notes", "Example", "Examples") or an
> > > anonymous section (name is "").
> >
> > yes, and the section type is checked in  _append_freeform()
> >
> > >> +
> > >> +    def __init__(self, parser):
> > >> +        self.parser = parser
> > >> +        self.symbol = None
> > >> +        self.body = QAPIDoc.Section()
> > >> +        # a dict {'arg': ArgSection, ...}
> > >
> > > Works.  Alternatevely:
> > >
> > >            # dict mapping parameter name to ArgSection
> >
> > ok
> >
> > >> +        self.args = OrderedDict()
> > >> +        # a list of Section
> > >> +        self.meta = []
> > >> +        # the current section
> > >> +        self.section = self.body
> > >> +        # associated expression and info (set by expression parser)
> > >
> > > "will be set by expression parser", or "to be set by expression
> parser"?
> >
> > ok
> >
> > >> +        self.expr = None
> > >> +        self.info = None
> > >
> > > Clearer now.  The one remark I still have is on the use of "meta" isn't
> > > obvious here.  I think a comment further up explaining the different
> > > kinds of sections would help.
> > >
> > > To be honest, I don't get what's "meta" about the "meta" sections :)
> >
> > Let's drop that distinction, it isn't necessary anymore
> >
> > >> +
> > >> +    def has_meta(self, name):
> > >> +        """Returns True if the doc has a meta section 'name'"""
> > >
> > > PEP257[1] demands imperative mood, and punctuation:
> > >
> > >            """Return True if the doc has a meta section 'name'."""
> > >
> > > Unfortunately, there seems to be no established convention for marking
> > > parameter names.  You put this one in single quotes, which works here,
> > > but could be confusing in other cases.  I'd sidestep it:
> > >
> > >            """Return True if we have a meta section with this name."""
> > >
> > > or
> > >
> > >            """Does a meta section with this name exist?"""
> >
> > ok
> >
> > >> +        for i in self.meta:
> > >> +            if i.name == name:
> > >> +                return True
> > >> +        return False
> > >> +
> > >> +    def append(self, line):
> > >> +        """Adds a # comment line, to be parsed and added to current
> section"""
> > >
> > > Imperative mood:
> > >
> > >     """Add a comment line, to be parsed and added to the current
> section."""
> > >
> > > However, we're not always adding to the current section, we can also
> > > start a new one.  The following avoids suggesting anything about the
> > > current section:
> > >
> > >     """Parse a comment line and add it to the documentation."""
> > >
> > > When the function name is a verb, and its doc string starts with a
> > > different verb, the name might be suboptimal.  Use your judgement.
> > >
> > > If this one-liner is too terse, we can try a multi-line doc string:
> > >
> > >     """Parse a comment line and add it to the documentation.
> > >
> > >     TODO should we tell more about how we parse?
> > >
> > >     Args:
> > >         line: the comment starting with '#', with the newline stripped.
> > >
> > >     Raises:
> > >         QAPISchemaError: TODO explain error conditions
> > >     """
> > >
> > > Format stolen from the Google Python Style Guide[2], because PEP257 is
> > > of not much help there.
> > >
> > > Once you start with such elaborate doc strings, pressure will likely
> > > mount to do the same elsewhere.  Could be a distraction right now.  Use
> > > your judgement.
> >
> > ok, thanks
> >
> > >> +        line = line[1:]
> > >> +        if not line:
> > >> +            self._append_freeform(line)
> > >> +            return
> > >> +
> > >> +        if line[0] != ' ':
> > >> +            raise QAPISchemaError(self.parser, "missing space after
> #")
> > >> +        line = line[1:]
> > >
> > > QAPISchemaError takes the error position from its QAPISchemaParser
> > > argument.  In other words, the error position depends on the state of
> > > the parser when it calls this function.  Turns out the position is the
> > > beginning of the comment.  Servicable here.  Action at a distance,
> > > though.
> > >
> > > Less strict:
> > >
> > >            # strip leading # and space
> > >            line = line[1:]
> > >            if line[0] == ' ':
> > >                line = line[1:]
> > >
> > > Also avoids special-casing empty comments.
> >
> > That would raise "IndexError: string index out of range" on empty comment
> > lines ("#")
>
> Fixable:
>                if line.startswith(' '):
>
> > Also I'd like to keep the error in case a space is missing (it caught
> some
> > already).
>
> On the one hand, I share your distaste for lack of space between # and
> comment text.  On the other hand, I dislike enforcing spacing
> inconsistently: enforce in doc comments, but not in other comments.
>
>
It's only enforced for doc comments, which I think is fair.


> > > Perhaps we should treat all leading whitespace the same (I'm not sure):
> > >
> > >            # strip leading # and whitespace
> > >            line = line[1:]
> > >            line = line.lstrip()
> >
> > That would break indentation in Example sections
>
> True; scratch the idea.
>
> > >> +
> > >> +        if self.symbol:
> > >> +            self._append_symbol_line(line)
> > >> +        elif (not self.body.content and
> > >> +              line.startswith("@") and line.endswith(":")):
> > >> +            self.symbol = line[1:-1]
> > >> +            if not self.symbol:
> > >> +                raise QAPISchemaError(self.parser, "Invalid symbol")
> > >
> > > Doesn't recognize the symbol when there's anything other than a single
> > > space between # and @.  Pathological case: '#\t@foo:' starting in
> column
> > > 6 looks exactly like '# @foo:', but doesn't work.  Fortunately, your
> > > code rejects the tab there.  Assigning meaning to (leading) whitespace
> > > may make sense, but it must be documented clearly.
> >
> > I think it's simpler to only accept a simple space (# + space + @ +
> symbol
> > + :), it will also lead to more uniform doc.
>
> Simpler is always an argument worth considering, but simpler isn't
> always better.  Experience with other languages has taught me to be
> carefule when making the kind or amount of whitespace significant.
>
> Kind of whitespace is not a usability issue with your proposal, because
> your patch rejects anything but space.
>
> Amount of whitespace is, because getting it wrong turns a symbol comment
> block into a free-form comment block, or a parameter section into
> free-form lines belonging to whatever section precedes it.
>
> As long as we reject definitions without a symbol comment, errors of the
> former kind will still be caught, although the error message could be
> confusing.
>
> As is, we don't reject definitions whose symbol comment fails to
> document all parameters.  Errors of the latter kind will therefore go
> undetected.  Trap for the unwary.  See also "@param2 is True" below.
>
> If we can't find a solution without such usability issues now, then I'm
> willing to settle for documentation warning unwary users, or even for a
> FIXME comment.
>

ok


>
> > > Doesn't recognize the symbol when there's crap after ":".  Confusing
> > > when it's invisible crap, i.e. trailing whitespace.  In general, it's
> > > best to parse left to right, like this:
> > >
> > >            elif not self.body.content and line.startswith('@'):
> > >                match = valid_name.match(line[1:]);
> > >                if not match:
> > >                    raise QAPISchemaError(self.parser, 'Invalid name')
> > >                if line.startswith(':', match.end(0)):
> > >                    raise QAPISchemaError(self.parser, 'Expected ":")
> >
> > Easier is simply to check :
> > if not line.endswith(":"):
> >                 raise QAPIParseError(self.parser, "Line should end with
> :")
> >
> > test added
>
> There's a reason we have a mountain of theory on parsing, built in
> decades of practice.  What starts "easier" than that generally ends up
> rewritten from scratch.  Anyway, let's continue.
>
> > > Here, the error position is clearly suboptimal: it's the beginning of
> > > the comment instead of where the actual error is, namely the beginning
> /
> > > the end of the name.  More of the same below.  The root cause is
> > > stacking parsers.  I'm not asking you to do anything about it at this
> > > time.
> > >
> > > Note my use of "name" rather than "symbol" in the error message.
> > > qapi-code-gen.txt talks about names, not symbols.  Consistent use of
> > > terminology matters.
> >
> > Agree, isn't "symbol" more appropriate for documentation though?
>
> Possibly.  But if it's an improvement for generated documentation, it
> surely is an improvement for hand-written documentation such as
> qapi-code-gen.txt and the comments in qapi*.py, too.  And the Python
> identifiers.  I have to admit that cools my enthusiasm for making the
> improvement right now :)
>
> > >> +        else:
> > >> +            self._append_freeform(line)
> > >> +
> > >> +    def _append_symbol_line(self, line):
> > >> +        name = line.split(' ', 1)[0]
> > >> +
> > >> +        if name.startswith("@") and name.endswith(":"):
> > >> +            line = line[len(name)+1:]
> > >> +            self._start_args_section(name[1:-1])
> > >
> > > Similar issue.  Left to right:
> > >
> > >            if re.match(r'\s*@', line):
> > >                match = valid_name.match(line[1:]);
> > >                if not match:
> > >                    raise QAPISchemaError(self.parser, 'Invalid
> parameter name')
> > >                line = line[match.end(0):]
> > >                if line.startswith(':'):
> > >                    raise QAPISchemaError(self.parser, 'Expected ":")
> > >                line = line[1:]
> > >                self._start_args_section(match.group(0))
> > >
> > > Even better would be factoring out the common code to parse '@foo:'.
> >
> > It's a valid case to have a section freeform comment starting with @foo,
> ex:
> >
> > @param: do this and than if
> >                @param2 is True
>
> Good point, but it's a language issue, not a parsing issue.
>
> The language issue is that we need to recognize a line "# @param: ..."
> as the start of a parameter section, while still permitting free-form
> lines like "# @param2 is True".
>
> This is a pretty convincing argument for requiring the ':'.  It can also
> be used as an argument for requiring exactly one space between # and @.
>
> How the language is parsed is completely orthogonal.
>
> >  I don't think it's worth to factor out the code to parse @foo vs @foo:,
> yet
> >
> > >> +        elif name in ("Returns:", "Since:",
> > >> +                      # those are often singular or plural
> > >> +                      "Note:", "Notes:",
> > >> +                      "Example:", "Examples:"):
> > >> +            line = line[len(name)+1:]
> > >> +            self._start_meta_section(name[:-1])
> > >
> > > Since we're re.match()ing already, here's how do to it that way:
> >
> > I don't follow your suggestion for the reason above,so I still have the
> > first word 'name'
> >
> > >            else
> > >                match =
> re.match(r'\s*(Returns|Since|Notes?|Examples?):', line)
> > >                if match:
> > >                    line = line[match.end(0):]
> > >                    self._start_meta_section(match.group(1))
> >
> > not really much nicer to me (especially because no match yet), I'll keep
> > the current code for now
> >
> > >> +
> > >> +        self._append_freeform(line)
> > >> +
> > >> +    def _start_args_section(self, name):
> > >> +        if not name:
> > >> +            raise QAPISchemaError(self.parser, "Invalid argument
> name")
> > >
> > > parameter name
> >
> > ok
> >
> > >> +        if name in self.args:
> > >> +            raise QAPISchemaError(self.parser, "'%s' arg duplicated"
> % name)
> > >
> > > Duplicate parameter name
> >
> > ok
> >
> > >> +        self.section = QAPIDoc.ArgSection(name)
> > >> +        self.args[name] = self.section
> > >> +
> > >> +    def _start_meta_section(self, name):
> > >> +        if name in ("Returns", "Since") and self.has_meta(name):
> > >> +            raise QAPISchemaError(self.parser,
> > >> +                                  "Duplicated '%s' section" % name)
> > >> +        self.section = QAPIDoc.Section(name)
> > >> +        self.meta.append(self.section)
> > >> +
> > >> +    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] \
> > >> +           and line and not line[0].isspace():
> > >
> > > PEP8[3] prefers parenthesises over backslash:
> > >
> > >            if (in_arg and self.section.content and not
> self.section.content[-1]
> > >                and line and not line[0].isspace()):
> >
> > yes, thanks
> >
> > >> +            # an empty line followed by a non-indented
> > >> +            # comment ends the argument section
> > >> +            self.section = self.body
> > >> +            self._append_freeform(line)
> > >> +            return
> > >
> > > Switching back to the self.body section like this reorders the
> > > documentation text.  I still think this is a terrible idea.  A dumb
> > > script is exceedingly unlikely to improve human-written doc comment
> text
> > > by reordering.  In the rare case it does, the doc comment source should
> > > be reordered.
> > >
> > > Here's an example where the doc generator happily creates
> unintelligible
> > > garbage if I format CpuDefinitionInfo's doc comment in a slightly off
> > > way:
> > >
> > >     ##
> > >     # @CpuDefinitionInfo:
> > >     #
> > >     # Virtual CPU definition.
> > >     #
> > >     # @name: the name of the CPU definition
> > >     #
> > >     # @migration-safe: #optional whether a CPU definition can be safely
> > >     # used for migration in combination with a QEMU compatibility
> > >     # machine when migrating between different QMU versions and between
> > >     # hosts with different sets of (hardware or software)
> > >     # capabilities.
> > >     #
> > >     # If not provided, information is not available and callers should
> > >     # not assume the CPU definition to be migration-safe. (since 2.8)
> > >     #
> > >     # @static: whether a CPU definition is static and will not change
> > >     # depending on QEMU version, machine type, machine options and
> > >     # accelerator options.  A static model is always
> > >     # migration-safe. (since 2.8)
> > >     #
> > >     # @unavailable-features: #optional List of properties that prevent
> > >     # the CPU model from running in the current host. (since 2.8)
> > >     #
> > >     # @unavailable-features is a list of QOM property names that
> > >     # represent CPU model attributes that prevent the CPU from running.
> > >     # If the QOM property is read-only, that means there's no known
> > >     # way to make the CPU model run in the current host.
> Implementations
> > >     # that choose not to provide specific information return the
> > >     # property name "type".
> > >     #
> > >     # If the property is read-write, it means that it MAY be possible
> > >     # to run the CPU model in the current host if that property is
> > >     # changed. Management software can use it as hints to suggest or
> > >     # choose an alternative for the user, or just to generate
> meaningful
> > >     # error messages explaining why the CPU model can't be used.
> > >     # If @unavailable-features is an empty list, the CPU model is
> > >     # runnable using the current host and machine-type.
> > >     # If @unavailable-features is not present, runnability
> > >     # information for the CPU is not available.
> > >     #
> > >     # Since: 1.2.0
> > >     ##
> > >     { 'struct': 'CpuDefinitionInfo',
> > >       'data': { 'name': 'str', '*migration-safe': 'bool', 'static':
> 'bool',
> > >                 '*unavailable-features': [ 'str' ] } }
> > >
> > > To detect the problem, you have to read the generated docs attentively.
> > > You know my opinion on our chances for that to happen during
> > > development.
> > >
> > > My point is not that we should support this doc comment format.  My
> > > point is that people could conceivably write something like it, and not
> > > get caught in patch review.
> > >
> > > I can see three ways out of this swamp:
> > >
> > > 1. Let sections continue until another one begins.
> >
> > but we have interleaved  sections, and no explicit "body" section tag,
> ex:
> >
> > ##
> > # @input-send-event:
> > #
> > # Send input event(s) to guest.
> > #
> > # @device: #optional display device to send event(s) to.
> > # @head: #optional head to send event(s) to, in case the
> > #        display device supports multiple scanouts.
> > # @events: List of InputEvent union.
> > #
> > # Returns: Nothing on success.
> > #
> > # The @device and @head parameters can be used to send the input event
> > # to specific input devices in case (a) multiple input devices of the
> > # same kind are added to the virtual machine and (b) you have
> > # configured input routing (see docs/multiseat.txt) for those input
> > # ....
> >
> > > 2. Make text between sections an error.
> > >
> > > 3. When a section ends, start a new anonymous section.
> >
> > It's not clear how to recognize when a section ends and append to comment
> > body.
> >
> > 2. Make text between sections an error.
> >
> > Sound like the best option to me. I'll fix the doc and add a check & test
> > for this common pattern though:
> >
> > ##
> > # @TestStruct:
> > #
> > # body with @var
> > #
> > # @integer: foo
> > #           blah
> > #
> > #           bao
> > #
> > # Let's catch this bad comment.
> > ##
>
> Go ahead.
>
> > > Can't say offhand which one will work best.
> > >
> > >> +        if in_arg or not self.section.name.startswith("Example"):
> > >> +            line = line.strip()
> > >
> > > Stripping whitespace is not "Markdown-like", because intendation
> carries
> > > meaning in Markdown.  Example:
> > >
> > > * First item in itemized list
> > > * Second item
> > >     * Sub-item of second item
> > >     * Another sub-item
> > > * Third item
> > >
> > > Stripping whitespace destroys the list structure.  If that's what you
> > > want, you get to document where your "Markdown-like" markup is unlike
> > > Markdown :)
> >
> > let's use "comment annotations" instead
>
> Okay.
>
> > > Is there a technical reason for stripping whitespace?
> > >
> > > See also discussion of space after # above.
> > >
> > >> +        self.section.append(line)
> > >> +
> > >> +
> > >>  class QAPISchemaParser(object):
> > >>
> > >>      def __init__(self, fp, previously_included=[], incl_info=None):
> > >> @@ -137,11 +240,18 @@ class QAPISchemaParser(object):
> > >>          self.line = 1
> > >>          self.line_pos = 0
> > >>          self.exprs = []
> > >> +        self.docs = []
> > >>          self.accept()
> > >>
> > >>          while self.tok is not None:
> > >>              info = {'file': fname, 'line': self.line,
> > >>                      'parent': self.incl_info}
> > >> +            if self.tok == '#' and self.val.startswith('##'):
> > >
> > > How can self.val *not* start with '##' here?
> >
> > you are right, unnecessary condition since we break get_expr() only in
> > this case now
> >
> > >> +                doc = self.get_doc()
> > >> +                doc.info = info
> > >
> > > Let's pass info as argument to get_doc(), so we don't have to dot into
> > > doc here.  get_doc() can avoid dotting into doc by passing info to the
> > > constructor.
> >
> > ok
> >
> > >> +                self.docs.append(doc)
> > >> +                continue
> > >> +
> > >>              expr = self.get_expr(False)
> > >>              if isinstance(expr, dict) and "include" in expr:
> > >>                  if len(expr) != 1:
> > >> @@ -160,6 +270,7 @@ class QAPISchemaParser(object):
> > >>                          raise QAPILineError(info, "Inclusion loop
> for %s"
> > >>                                              % include)
> > >>                      inf = inf['parent']
> > >> +
> > >>                  # skip multiple include of the same file
> > >>                  if incl_abs_fname in previously_included:
> > >>                      continue
> > >> @@ -171,12 +282,38 @@ class QAPISchemaParser(object):
> > >>                  exprs_include = QAPISchemaParser(fobj,
> previously_included,
> > >>                                                   info)
> > >>                  self.exprs.extend(exprs_include.exprs)
> > >> +                self.docs.extend(exprs_include.docs)
> > >>              else:
> > >>                  expr_elem = {'expr': expr,
> > >>                               'info': info}
> > >> +                if self.docs and not self.docs[-1].expr:
> > >> +                    self.docs[-1].expr = expr
> > >> +                    expr_elem['doc'] = self.docs[-1]
> > >> +
> > >
> > > Attaches the expression to the last doc comment that doesn't already
> > > have one.  A bit sloppy, because there could be non-doc comments in
> > > between, or the doc comment could be in another file.  It'll do, at
> > > least for now.
> >
> > I extended the condition to check it attaches the doc from the same file
> >
> > >>                  self.exprs.append(expr_elem)
> > >>
> > >> -    def accept(self):
> > >> +    def get_doc(self):
> > >> +        if self.val != '##':
> > >> +            raise QAPISchemaError(self, "Junk after '##' at start of
> "
> > >> +                                  "documentation comment")
> > >> +
> > >> +        doc = QAPIDoc(self)
> > >> +        self.accept(False)
> > >> +        while self.tok == '#':
> > >> +            if self.val.startswith('##'):
> > >> +                # End of doc comment
> > >> +                if self.val != '##':
> > >> +                    raise QAPISchemaError(self, "Junk after '##' at
> end of "
> > >> +                                          "documentation comment")
> > >> +                self.accept()
> > >> +                return doc
> > >> +            else:
> > >> +                doc.append(self.val)
> > >> +            self.accept(False)
> > >> +
> > >> +        raise QAPISchemaError(self, "Documentation comment must end
> with '##'")
> > >
> > > Let's put this after accept, next to the other get_FOO().
> >
> > ok
> >
> > >> +
> > >> +    def accept(self, skip_comment=True):
> > >>          while True:
> > >>              self.tok = self.src[self.cursor]
> > >>              self.pos = self.cursor
> > >> @@ -184,7 +321,13 @@ class QAPISchemaParser(object):
> > >>              self.val = None
> > >>
> > >>              if self.tok == '#':
> > >> +                if self.src[self.cursor] == '#':
> > >> +                    # Start of doc comment
> > >> +                    skip_comment = False
> > >>                  self.cursor = self.src.find('\n', self.cursor)
> > >> +                if not skip_comment:
> > >> +                    self.val = self.src[self.pos:self.cursor]
> > >> +                    return
> > >>              elif self.tok in "{}:,[]":
> > >>                  return
> > >>              elif self.tok == "'":
> > >
> > > Copied from review of v3, so I don't forget:
> > >
> > > Comment tokens are thrown away as before, except when the parser asks
> > > for them by passing skip_comment=False, or when the comment token
> starts
> > > with ##.  The parser asks while parsing a doc comment, in get_doc().
> > >
> > > This is a backchannel from the parser to the lexer.  I'd rather avoid
> > > such lexer hacks, but I guess we can address that on top.
> >
> > I don't have a good solution to that
>
> I have an idea which may or may not work.  I can explore it on top.
>
>
thanks


> > > A comment starting with ## inside an expression is now a syntax error.
> > > For instance, input
> > >
> > >     {
> > >     ##
> > >
> > > yields
> > >
> > >     /dev/stdin:2:1: Expected string or "}"
> > >
> > > Rather unfriendly error message, but we can fix that on top.
> > >
> > >> @@ -713,7 +856,7 @@ def check_keys(expr_elem, meta, required,
> optional=[]):
> > >>                                  % (key, meta, name))
> > >>
> > >>
> > >> -def check_exprs(exprs):
> > >> +def check_exprs(exprs, strict_doc):
> > >
> > > Note: strict_doc=False unless this is qapi2texi.py.
> >
> > For tests reasons: we may want to fix the test instead, or have a flag
> > QAPI_CHECK/NOSTRICT_DOC=1 or an option.
> >
> > >>      global all_names
> > >>
> > >>      # Learn the types and check for valid expression keys
> > >> @@ -722,6 +865,11 @@ def check_exprs(exprs):
> > >>      for expr_elem in exprs:
> > >>          expr = expr_elem['expr']
> > >>          info = expr_elem['info']
> > >> +
> > >> +        if strict_doc and 'doc' not in expr_elem:
> > >> +            raise QAPILineError(info,
> > >> +                                "Expression missing documentation
> comment")
> > >> +
> > >
> > > Why should we supress this error in programs other than qapi2texi.py?
> >
> > because the tests don't have comments
>
> Good point :)
>
> Can we come up with a brief comment explaining this?
>
> I fixed the tests instead (after all, we have complete control on what to
accept or not. that was boring but now it's done)


> > > Can't see a test for this one.
> >
> > because tests aren't strict :)
> >
> > I guess you'll tell me to fix the tests instead, so I did that in the
> next
> > series.
> >
> > >>          if 'enum' in expr:
> > >>              check_keys(expr_elem, 'enum', ['data'], ['prefix'])
> > >>              add_enum(expr['enum'], info, expr['data'])
> > >> @@ -780,6 +928,63 @@ def check_exprs(exprs):
> > >>      return exprs
> > >>
> > >>
> > >> +def check_simple_doc(doc):
> > >
> > > You call this "free-form" elsewhere.  Pick one name and stick to it.
> > > I think free-form is more descriptive than simple.
> >
> > ok
> >
> > >> +    if doc.symbol:
> > >> +        raise QAPILineError(doc.info,
> > >> +                            "'%s' documention is not followed by the
> definition"
> > >> +                            % doc.symbol)
> > >
> > > "Documentation for %s is not ..."
> >
> > ok
> >
> > >> +
> > >> +    body = str(doc.body)
> > >> +    if re.search(r'@\S+:', body, re.MULTILINE):
> > >> +        raise QAPILineError(doc.info,
> > >> +                            "Document body cannot contain @NAME:
> sections")
> > >> +
> > >> +
> > >> +def check_expr_doc(doc, expr, info):
> > >
> > > You call this "symbol" elsewhere.  I think "definition" would be better
> > > than either.
> >
> > ok
> >
> > >> +    for i in ('enum', 'union', 'alternate', 'struct', 'command',
> 'event'):
> > >> +        if i in expr:
> > >> +            meta = i
> > >> +            break
> > >> +
> > >> +    name = expr[meta]
> > >> +    if doc.symbol != name:
> > >> +        raise QAPILineError(info, "Definition of '%s' follows
> documentation"
> > >> +                            " for '%s'" % (name, doc.symbol))
> > >> +    if doc.has_meta('Returns') and 'command' not in expr:
> > >> +        raise QAPILineError(info, "Invalid return documentation")
> > >
> > > Suggest something like "'Returns:' is only valid for commands".
> > >
> > > We accept 'Returns:' even when the command doesn't return anything,
> > > because we currently use it to document errors, too.  Can't say I like
> > > that, but it's out of scope here.
> >
> > ok
> >
> > >> +
> > >> +    doc_args = set(doc.args.keys())
> > >> +    if meta == 'union':
> > >> +        data = expr.get('base', [])
> > >> +    else:
> > >> +        data = expr.get('data', [])
> > >> +    if isinstance(data, dict):
> > >> +        data = data.keys()
> > >> +    args = set([name.strip('*') for name in data])
> > >
> > > Not fixed since v3:
> > >
> > > * Flat union where 'base' is a string, e.g. union UserDefFlatUnion in
> > >   qapi-schema-test.json has base 'UserDefUnionBase', args is set(['a',
> > >   'B', 'e', 'D', 'f', 'i', 'o', 'n', 's', 'r', 'U'])
> > >
> > > * Command where 'data' is a string, e.g. user_def_cmd0 in
> > >   qapi-schema-test.json has data 'Empty2', args is set(['E', 'm', 'p',
> > >   '2', 't', 'y'])
> > >
> > > * Event where 'data' is a string, no test case handy (hole in test
> > >   coverage)
> >
> > ok, I changed it that way, that fixes it:
> > +    if isinstance(data, list):
> > +        args = set([name.strip('*') for name in data])
> > +    else:
> > +        args = set()
>
> Uh, sure this does the right thing when data is a dict?
>

yes, the line above takes care of extracting the keys in data.


>
> > >> +    if meta == 'alternate' or \
> > >> +       (meta == 'union' and not expr.get('discriminator')):
> > >> +        args.add('type')
> > >
> > > As explained in review of v3, this is only a subset of the real set of
> > > members.  Computing the exact set is impractical when working with the
> > > abstract syntax tree.  I believe we'll eventually have to rewrite this
> > > code to work with the QAPISchemaEntity instead.
> >
> > I don't think we want to list all the members as this would lead to
> > duplicated documentation. Instead it should document only the members of
> > the expr being defined.
>
> You're sticking to established practice, which makes lots of sense.
> However, established practice is a lot more clear for simple cases than
> for things like members inherited from base types, variant members and
> such.  At some point, we'll have to figure out how we want not so simple
> cases documented.  Until then, we can't really decide how to check
> documentation completeness.
>
> >                         In which case, it looks like this check is good
> > enough, no?
>
> For now, yes.  Later on, maybe.
>
> Let's document the limitation in a comment and the commit message, and
> move on.
>

I'd appreciate your help to list the limitations in the commit message, as
I have not as thorough understanding as you, and even worse I often don't
use the same name for the various concepts.


>
> > >> +    if not doc_args.issubset(args):
> > >> +        raise QAPILineError(info, "Members documentation is not a
> subset of"
> > >> +                            " API %r > %r" % (list(doc_args),
> list(args)))
> > >> +
> > >> +
> > >> +def check_docs(docs):
> > >> +    for doc in docs:
> > >> +        for section in doc.args.values() + doc.meta:
> > >> +            content = str(section)
> > >> +            if not content or content.isspace():
> > >> +                raise QAPILineError(doc.info,
> > >> +                                    "Empty doc section '%s'" %
> section.name)
> > >> +
> > >> +        if not doc.expr:
> > >> +            check_simple_doc(doc)
> > >> +        else:
> > >> +            check_expr_doc(doc, doc.expr, doc.info)
> > >> +
> > >> +    return docs
> > >> +
> > >> +
> > >>  #
> > >>  # Schema compiler frontend
> > >>  #
> > >> @@ -1249,9 +1454,11 @@ class QAPISchemaEvent(QAPISchemaEntity):
> > >>
> > >>
> > >>  class QAPISchema(object):
> > >> -    def __init__(self, fname):
> > >> +    def __init__(self, fname, strict_doc=False):
> > >>          try:
> > >> -            self.exprs = check_exprs(QAPISchemaParser(open(fname,
> "r")).exprs)
> > >> +            parser = QAPISchemaParser(open(fname, "r"))
> > >> +            self.exprs = check_exprs(parser.exprs, strict_doc)
> > >> +            self.docs = check_docs(parser.docs)
> > >>              self._entity_dict = {}
> > >>              self._predefining = True
> > >>              self._def_predefineds()
> > >> diff --git a/scripts/qapi2texi.py b/scripts/qapi2texi.py
> > >> new file mode 100755
> > >> index 0000000..0cec43a
> > >> --- /dev/null
> > >> +++ b/scripts/qapi2texi.py
> > >
> > > Still only skimming this one.
> > >
> > >> @@ -0,0 +1,331 @@
> > >> +#!/usr/bin/env python
> > >> +# QAPI texi generator
> > >> +#
> > >> +# This work is licensed under the terms of the GNU LGPL, version 2+.
> > >> +# See the COPYING file in the top-level directory.
> > >> +"""This script produces the documentation of a qapi schema in
> texinfo format"""
> > >> +import re
> > >> +import sys
> > >> +
> > >> +import qapi
> > >> +
> > >> +COMMAND_FMT = """
> > >> +@deftypefn {type} {{{ret}}} {name} @
> > >> +{{{args}}}
> > >> +
> > >> +{body}
> > >> +
> > >> +@end deftypefn
> > >> +
> > >> +""".format
> > >> +
> > >> +ENUM_FMT = """
> > >> +@deftp Enum {name}
> > >> +
> > >> +{body}
> > >> +
> > >> +@end deftp
> > >> +
> > >> +""".format
> > >> +
> > >> +STRUCT_FMT = """
> > >> +@deftp {{{type}}} {name} @
> > >> +{{{attrs}}}
> > >> +
> > >> +{body}
> > >> +
> > >> +@end deftp
> > >> +
> > >> +""".format
> > >> +
> > >> +EXAMPLE_FMT = """@example
> > >> +{code}
> > >> +@end example
> > >> +""".format
> > >> +
> > >> +
> > >> +def subst_strong(doc):
> > >> +    """Replaces *foo* by @strong{foo}"""
> > >> +    return re.sub(r'\*([^_\n]+)\*', r'@emph{\1}', doc)
> > >> +
> > >> +
> > >> +def subst_emph(doc):
> > >> +    """Replaces _foo_ by @emph{foo}"""
> > >> +    return re.sub(r'\s_([^_\n]+)_\s', r' @emph{\1} ', doc)
> > >> +
> > >> +
> > >> +def subst_vars(doc):
> > >> +    """Replaces @var by @code{var}"""
> > >> +    return re.sub(r'@([\w-]+)', r'@code{\1}', doc)
> > >> +
> > >> +
> > >> +def subst_braces(doc):
> > >> +    """Replaces {} with @{ @}"""
> > >> +    return doc.replace("{", "@{").replace("}", "@}")
> > >> +
> > >> +
> > >> +def texi_example(doc):
> > >> +    """Format @example"""
> > >> +    doc = subst_braces(doc).strip('\n')
> > >> +    return EXAMPLE_FMT(code=doc)
> > >> +
> > >> +
> > >> +def texi_comment(doc):
> > >> +    """
> > >> +    Format a comment
> > >> +
> > >> +    Lines starting with:
> > >> +    - |: generates an @example
> > >> +    - =: generates @section
> > >> +    - ==: generates @subsection
> > >> +    - 1. or 1): generates an @enumerate @item
> > >> +    - o/*/-: generates an @itemize list
> > >> +    """
> > >> +    lines = []
> > >> +    doc = subst_braces(doc)
> > >> +    doc = subst_vars(doc)
> > >> +    doc = subst_emph(doc)
> > >> +    doc = subst_strong(doc)
> > >> +    inlist = ""
> > >> +    lastempty = False
> > >> +    for line in doc.split('\n'):
> > >> +        empty = line == ""
> > >> +
> > >> +        if line.startswith("| "):
> > >> +            line = EXAMPLE_FMT(code=line[2:])
> > >> +        elif line.startswith("= "):
> > >> +            line = "@section " + line[2:]
> > >> +        elif line.startswith("== "):
> > >> +            line = "@subsection " + line[3:]
> > >> +        elif re.match("^([0-9]*[.)]) ", line):
> > >> +            if not inlist:
> > >> +                lines.append("@enumerate")
> > >> +                inlist = "enumerate"
> > >> +            line = line[line.find(" ")+1:]
> > >> +            lines.append("@item")
> > >> +        elif re.match("^[o*-] ", line):
> > >> +            if not inlist:
> > >> +                lines.append("@itemize %s" % {'o': "@bullet",
> > >> +                                              '*': "@minus",
> > >> +                                              '-': ""}[line[0]])
> > >> +                inlist = "itemize"
> > >> +            lines.append("@item")
> > >> +            line = line[2:]
> > >> +        elif lastempty and inlist:
> > >> +            lines.append("@end %s\n" % inlist)
> > >> +            inlist = ""
> > >> +
> > >> +        lastempty = empty
> > >> +        lines.append(line)
> > >> +
> > >> +    if inlist:
> > >> +        lines.append("@end %s\n" % inlist)
> > >> +    return "\n".join(lines)
> > >> +
> > >> +
> > >> +def texi_args(expr, key="data"):
> > >> +    """
> > >> +    Format the functions/structure/events.. arguments/members
> > >> +    """
> > >> +    if key not in expr:
> > >> +        return ""
> > >> +
> > >> +    args = expr[key]
> > >> +    arg_list = []
> > >> +    if isinstance(args, str):
> > >> +        arg_list.append(args)
> > >> +    else:
> > >> +        for name, typ in args.iteritems():
> > >> +            # optional arg
> > >> +            if name.startswith("*"):
> > >> +                name = name[1:]
> > >> +                arg_list.append("['%s': @var{%s}]" % (name, typ))
> > >> +            # regular arg
> > >> +            else:
> > >> +                arg_list.append("'%s': @var{%s}" % (name, typ))
> > >
> > > Inappropriate use of @var.  @var is for metasyntactic variables,
> > > i.e. something that stands for another piece of text.  typ isn't, it's
> > > the name of a specific QAPI type.  I think you should use @code.
> >
> > yes
> >
> > > This is the reason why the type names in qemu-qmp-ref.txt are often
> > > mangled, e.g.
> > >
> > >  -- Struct: VersionInfo { 'qemu': VERSIONTRIPLE, 'package': STR }
> >
> > Right, we have format issue here. If we change it for @code, we get
> > additional quotes in the text format. The simpler is to use no format or
> > @t{}
>
> Pick something you like.
>
> > >> +
> > >> +    return ", ".join(arg_list)
> > >> +
> > >> +
> > >> +def texi_body(doc):
> > >> +    """
> > >> +    Format the body of a symbol documentation:
> > >> +    - a table of arguments
> > >> +    - followed by "Returns/Notes/Since/Example" sections
> > >> +    """
> > >> +    def _section_order(section):
> > >> +        return {"Returns": 0,
> > >> +                "Note": 1,
> > >> +                "Notes": 1,
> > >> +                "Since": 2,
> > >> +                "Example": 3,
> > >> +                "Examples": 3}[section]
> > >> +
> > >> +    body = "@table @asis\n"
> > >> +    for arg, section in doc.args.iteritems():
> > >> +        desc = str(section)
> > >> +        opt = ''
> > >> +        if desc.startswith("#optional"):
> > >> +            desc = desc[10:]
> > >> +            opt = ' *'
> > >> +        elif desc.endswith("#optional"):
> > >> +            desc = desc[:-10]
> > >> +            opt = ' *'
> > >> +        body += "@item @code{'%s'}%s\n%s\n" % (arg, opt,
> texi_comment(desc))
> > >> +    body += "@end table\n"
> > >> +    body += texi_comment(str(doc.body))
> > >> +
> > >> +    meta = sorted(doc.meta, key=lambda i: _section_order(i.name))
> > >> +    for section in meta:
> > >> +        name, doc = (section.name, str(section))
> > >> +        func = texi_comment
> > >> +        if name.startswith("Example"):
> > >> +            func = texi_example
> > >> +
> > >> +        body += "\n@quotation %s\n%s\n@end quotation" % \
> > >> +                (name, func(doc))
> > >> +    return body
> > >> +
> > >> +
> > >> +def texi_alternate(expr, doc):
> > >> +    """
> > >> +    Format an alternate to texi
> > >> +    """
> > >> +    args = texi_args(expr)
> > >> +    body = texi_body(doc)
> > >> +    return STRUCT_FMT(type="Alternate",
> > >> +                      name=doc.symbol,
> > >> +                      attrs="[ " + args + " ]",
> > >> +                      body=body)
> > >> +
> > >> +
> > >> +def texi_union(expr, doc):
> > >> +    """
> > >> +    Format an union to texi
> > >
> > > I think it's "a union".
> >
> > ok
> >
> > >> +    """
> > >> +    attrs = "@{ " + texi_args(expr, "base") + " @}"
> > >> +    discriminator = expr.get("discriminator")
> > >> +    if not discriminator:
> > >> +        union = "Flat Union"
> > >> +        discriminator = "type"
> > >> +    else:
> > >> +        union = "Union"
> > >
> > > Condition is backwards.
> >
> > fixed
> >
> > >> +    attrs += " + '%s' = [ " % discriminator
> > >> +    attrs += texi_args(expr, "data") + " ]"
> > >> +    body = texi_body(doc)
> > >> +
> > >> +    return STRUCT_FMT(type=union,
> > >> +                      name=doc.symbol,
> > >> +                      attrs=attrs,
> > >> +                      body=body)
> > >
> > > You're inventing syntax here.  Example output:
> > >
> > >  -- Union: QCryptoBlockOpenOptions { QCryptoBlockOptionsBase } +
> > >           'format' = [ 'qcow': QCRYPTOBLOCKOPTIONSQCOW, 'luks':
> > >           QCRYPTOBLOCKOPTIONSLUKS ]
> > >
> > >      The options that are available for all encryption formats when
> > >      opening an existing volume
> > >           Since: 2.6
> > >
> > >  -- Flat Union: ImageInfoSpecific { } + 'type' = [ 'qcow2':
> > >           IMAGEINFOSPECIFICQCOW2, 'vmdk': IMAGEINFOSPECIFICVMDK,
> 'luks':
> > >           QCRYPTOBLOCKINFOLUKS ]
> > >
> > >      A discriminated record of image format specific information
> > >      structures.
> > >           Since: 1.7
> > >
> > > Note that QCryptoBlockOpenOptions is actually a flat union, and
> > > ImageInfoSpecific a simple union.  As I said, the condition is
> > > backwards.
> > >
> > > The meaning of the attrs part is unobvious.  Familiarity with schema
> > > syntax doesn't really help.
> > >
> > > Could we simply use schema syntax here?
> >
> > Union: QCryptoBlockOpenOptions {
> > 'base': QCryptoBlockOptionsBase,
> > 'discriminator': 'format',
> > 'data':  { 'qcow': QCryptoBlockOptionsQCow, 'luks':
> > QCryptoBlockCreateOptionsLUKS }
> >
> > }
> >
> > Doesn't look obvious either and pollute the documentation with
> > schema-specific parameters.
>
> The schema syntax for flat unions is pretty horrid :)  I hope to improve
> it, but there's more urgent fish to fry.
>
> > > If not: whatever format you use, you first need to explain it.
> >
> > I am not sure my solution is the best and will remain, but ok let's try
> to
> > document it for now.
>
> Before you pour time into documenting what you have, we should probably
> discuss what we need.
>

Can this be marked as limitation and improved laster? The series is big,
patch is getting bigger over time, it's quite hard to handle all your
requirements in one go.

> >> +
> > >> +
> > >> +def texi_enum(expr, doc):
> > >> +    """
> > >> +    Format an enum to texi
> > >> +    """
> > >> +    for i in expr['data']:
> > >> +        if i not in doc.args:
> > >> +            doc.args[i] = ''
> > >> +    body = texi_body(doc)
> > >> +    return ENUM_FMT(name=doc.symbol,
> > >> +                    body=body)
> > >> +
> > >> +
> > >> +def texi_struct(expr, doc):
> > >> +    """
> > >> +    Format a struct to texi
> > >> +    """
> > >> +    args = texi_args(expr)
> > >> +    body = texi_body(doc)
> > >> +    attrs = "@{ " + args + " @}"
> > >> +    base = expr.get("base")
> > >> +    if base:
> > >> +        attrs += " + %s" % base
> > >> +    return STRUCT_FMT(type="Struct",
> > >> +                      name=doc.symbol,
> > >> +                      attrs=attrs,
> > >> +                      body=body)
> > >
> > > More syntax invention.  Example output:
> > >
> > >  -- Struct: BlockdevOptionsReplication { 'mode': REPLICATIONMODE,
> > >           ['top-id': STR] } + BlockdevOptionsGenericFormat
> > >
> > >      ''mode''
> > >           the replication mode
> > >      ''top-id'' *
> > >           In secondary mode, node name or device ID of the root node
> who
> > >           owns the replication node chain.  Must not be given in
> primary
> > >           mode.
> > >      Driver specific block device options for replication
> > >           Since: 2.8
> > >
> > > Meaning of the attrs part is perhaps more guessable here, but it's
> still
> > > guesswork.
> > >
> > > The meaning of * after ''top-id'' is also unobvious.
> > >
> > > Note the redundancy between the attrs part and the body: both state
> > > member names and optionalness.  The body doesn't state member types and
> > > base type.  If we fixed that, we could drop the attrs part and save us
> > > the trouble of defining and explaining a syntax for it.
> > >
> > > Let me take a step back.  This document is about the QMP wire format.
> > > There are no such things as simple and flat unions on the wire, only
> > > JSON objects.  QMP introspection duly describes a type's JSON objects,
> > > not how it's defined in QAPI.  I think QMP documentation should ideally
> > > do the same.
> > >
> > > QMP introspection uses a common structure for struct, simple and flat
> > > union: common members, variants, and if variants, then the common
> member
> > > that serves as tag.  See introspect.json for details.
> > >
> > > Base types are flattened away.  Appropriate for introspection, but
> > > documentation shouldn't do that.
> > >
> > > I wrote "ideally" because it's probably too big a step.  I'm willing to
> > > settle for something less than ideal.
> >
> > I don't have clear idea what to do here, so if we can leave that for
> later,
> > that would be nice for me (I am already spending more time than I
> imagined
> > I would on doc stuff)
>
> Me too, if that's any consolation...
>
> Perhaps I can find a bit of time to think so I can propose what we could
> do.
>
> > >> +
> > >> +
> > >> +def texi_command(expr, doc):
> > >> +    """
> > >> +    Format a command to texi
> > >> +    """
> > >> +    args = texi_args(expr)
> > >> +    ret = expr["returns"] if "returns" in expr else ""
> > >> +    body = texi_body(doc)
> > >> +    return COMMAND_FMT(type="Command",
> > >> +                       name=doc.symbol,
> > >> +                       ret=ret,
> > >> +                       args="(" + args + ")",
> > >> +                       body=body)
> > >> +
> > >> +
> > >> +def texi_event(expr, doc):
> > >> +    """
> > >> +    Format an event to texi
> > >> +    """
> > >> +    args = texi_args(expr)
> > >> +    body = texi_body(doc)
> > >> +    return COMMAND_FMT(type="Event",
> > >> +                       name=doc.symbol,
> > >> +                       ret="",
> > >> +                       args="(" + args + ")",
> > >> +                       body=body)
> > >> +
> > >> +
> > >> +def texi_expr(expr, doc):
> > >> +    """
> > >> +    Format an expr to texi
> > >> +    """
> > >> +    (kind, _) = expr.items()[0]
> > >> +
> > >> +    fmt = {"command": texi_command,
> > >> +           "struct": texi_struct,
> > >> +           "enum": texi_enum,
> > >> +           "union": texi_union,
> > >> +           "alternate": texi_alternate,
> > >> +           "event": texi_event}
> > >> +    try:
> > >> +        fmt = fmt[kind]
> > >> +    except KeyError:
> > >> +        raise ValueError("Unknown expression kind '%s'" % kind)
> > >
> > > The try / except converts one kind of error into another.  What does
> > > that buy us?  As far as I can tell, this shouldn't ever happen.
> >
> > dropped
> >
> > >> +
> > >> +    return fmt(expr, doc)
> > >> +
> > >> +
> > >> +def texi(docs):
> > >> +    """
> > >> +    Convert QAPI schema expressions to texi documentation
> > >> +    """
> > >> +    res = []
> > >> +    for doc in docs:
> > >> +        expr = doc.expr
> > >> +        if not expr:
> > >> +            res.append(texi_body(doc))
> > >> +            continue
> > >> +        try:
> > >> +            doc = texi_expr(expr, doc)
> > >> +            res.append(doc)
> > >> +        except:
> > >> +            print >>sys.stderr, "error at @%s" % doc.info
> > >> +            raise
> > >> +
> > >> +    return '\n'.join(res)
> > >> +
> > >> +
> > >> +def main(argv):
> > >> +    """
> > >> +    Takes schema argument, prints result to stdout
> > >> +    """
> > >> +    if len(argv) != 2:
> > >> +        print >>sys.stderr, "%s: need exactly 1 argument: SCHEMA" %
> argv[0]
> > >> +        sys.exit(1)
> > >> +
> > >> +    schema = qapi.QAPISchema(argv[1], strict_doc=True)
> > >> +    print texi(schema.docs)
> > >> +
> > >> +
> > >> +if __name__ == "__main__":
> > >> +    main(sys.argv)
> > >> diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
> > >> index 2841c51..8bc963e 100644
> > >> --- a/docs/qapi-code-gen.txt
> > >> +++ b/docs/qapi-code-gen.txt
> > >> @@ -45,16 +45,13 @@ QAPI parser does not).  At present, there is no
> place where a QAPI
> > >>  schema requires the use of JSON numbers or null.
> > >>
> > >>  Comments are allowed; anything between an unquoted # and the
> following
> > >> -newline is ignored.  Although there is not yet a documentation
> > >> -generator, a form of stylized comments has developed for consistently
> > >> -documenting details about an expression and when it was added to the
> > >> -schema.  The documentation is delimited between two lines of ##, then
> > >> -the first line names the expression, an optional overview is
> provided,
> > >> -then individual documentation about each member of 'data' is
> provided,
> > >> -and finally, a 'Since: x.y.z' tag lists the release that introduced
> > >> -the expression.  Optional members are tagged with the phrase
> > >> -'#optional', often with their default value; and extensions added
> > >> -after the expression was first released are also given a '(since
> > >> +newline is ignored.  The documentation is delimited between two lines
> > >> +of ##, then the first line names the expression, an optional overview
> > >> +is provided, then individual documentation about each member of
> 'data'
> > >> +is provided, and finally, a 'Since: x.y.z' tag lists the release that
> > >> +introduced the expression.  Optional members are tagged with the
> > >> +phrase '#optional', often with their default value; and extensions
> > >> +added after the expression was first released are also given a
> '(since
> > >>  x.y.z)' comment.  For example:
> > >>
> > >>      ##
> > >> @@ -73,12 +70,49 @@ x.y.z)' comment.  For example:
> > >>      #           (Since 2.0)
> > >>      #
> > >>      # Since: 0.14.0
> > >> +    #
> > >> +    # Notes: You can also make a list:
> > >> +    #        - with items
> > >> +    #        - like this
> > >> +    #
> > >> +    # Example:
> > >> +    #
> > >> +    # -> { "execute": ... }
> > >> +    # <- { "return": ... }
> > >> +    #
> > >>      ##
> > >>      { 'struct': 'BlockStats',
> > >>        'data': {'*device': 'str', 'stats': 'BlockDeviceStats',
> > >>                 '*parent': 'BlockStats',
> > >>                 '*backing': 'BlockStats'} }
> > >
> > > This example gives an idea of how to document struct types.  But the
> > > reader is left guessing how to document other kinds of definitions.  In
> > > particular, there's no mention of "Returns", "Note" and "Examples"
> > > sections anywhere in this file.  As is, this could do for a tutorial,
> > > but this file is a *reference*, not a tutorial.
> > >
> > > For a reference, we need to be more thorough.  A doc comments section
> on
> > > its own seems advisable.
> > >
> > > I guess doc comment examples are best added to the schema code examples
> > > in sections === Struct types ===, ..., === Events ===.
> > >
> > > Careful review for completeness is advised.
> >
> > So far we did quite fine with the generic example. I am not convinced we
> > need to have doc comments for all kinds of types, it looks redundant to
> me.
>
> Well, so far we didn't require people to write well-formed doc comments.
>
> Without such comments in qapi-code-gen.txt's examples, they become
> incomplete.  Pity, because I like to feed them to the QAPI generators;
> extracted like this:
>
>     $ sed '1,/\$ cat example-schema.json/d;/^[^ ]/,$d;s/^    //'
> ../docs/qapi-code-gen.txt >example-schema.json
>
> > >> +It's also possible to create documentation sections, such as:
> > >> +
> > >> +    ##
> > >> +    # = Section
> > >> +    # == Subsection
> > >> +    #
> > >> +    # Some text foo with *strong* and _emphasis_
> > >> +    # 1. with a list
> > >> +    # 2. like that
> > >> +    #
> > >> +    # And some code:
> > >> +    # | $ echo foo
> > >> +    # | -> do this
> > >> +    # | <- get that
> > >> +    #
> > >> +    ##
> > >> +
> > >> +Text *foo* and _foo_ are for "strong" and "emphasis" styles (they do
> > >> +not work over multiple lines). @foo is used to reference a symbol.
> > >> +
> > >> +Lines starting with the following characters and a space:
> > >> +- | are examples
> > >> +- = are top section
> > >> +- == are subsection
> > >> +- X. or X) are enumerations (X is any number)
> > >> +- o/*/- are itemized list
> > >> +
> > >
> > > Is this doc markup documentation complete?
> >
> > I think so
> >
> > > Is it related to any existing text markup language?  Hmm, the commit
> > > message calls it "Markdown-like".  Should we mention that here?
> >
> > I'll use "annotations" instead in the doc
> >
> > > Is all markup valid in all contexts?  For instance, is = Section valid
> > > in symbol blocks?  What does | (or any markup for that matter) do
> within
> > > an Example section?
> >
> > Example is verbatim<

-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Qemu-devel] [PATCH v5 13/17] qapi: add qapi2texi script
  2016-12-06 13:07         ` Marc-André Lureau
@ 2016-12-07 16:05           ` Markus Armbruster
  0 siblings, 0 replies; 48+ messages in thread
From: Markus Armbruster @ 2016-12-07 16:05 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

Marc-André Lureau <marcandre.lureau@gmail.com> writes:

> Hi
>
> On Tue, Dec 6, 2016 at 2:50 PM Markus Armbruster <armbru@redhat.com> wrote:
>
>> I had to resort to diff to find your replies, and massage the text
>> manually to produce a readable reply myself.  Please quote the usual
>> way.
>>
>>
> I'd have to switch to something else than gmail (which bothers me for
> various reasons, let's not discuss the merits of various mail clients
> please ;) In general, I don't have problems, but this mail is rather big,
> sorry for the inconvenience..

This time, it was only a bit of mojibake and lots of unwanted line wraps
%-)

I'll try to cut unnecessary quoted material this time.

>> Markus Armbruster <armbru@redhat.com> writes:
>> >
>> > > Marc-André Lureau <marcandre.lureau@redhat.com> writes:
>> > >
>> > >> As the name suggests, the qapi2texi script converts JSON QAPI
>> > >> description into a texi file suitable for different target
>> > >> formats (info/man/txt/pdf/html...).
[...]
>> > > Missing: a brief discussion of deficiencies.  These include:
>> > >
>> > > * The generated QMP documentation includes internal types
>> > >
>> > >   We use qapi-schema.json both for defining the external QMP interface
>> > >   and for defining internal types.  qmp-introspect.py carefully
>> > >   separates the two, to not expose internal types.  qapi2texi.py happily
>> > >   exposes everything.
>> > >
>> > >   Currently, about a fifth of the types in the generated docs are
>> > >   internal:
>> > >
>> > >       AcpiTableOptions
>> > >       BiosAtaTranslation
>> > >       BlockDeviceMapEntry
>> > >       COLOMessage
>> > >       COLOMode
>> > >       DummyForceArrays
>> > >       FailoverStatus
>> > >       FloppyDriveType
>> > >       ImageCheck
>> > >       LostTickPolicy
>> > >       MapEntry
>> > >       MigrationParameter
>> > >       NetClientDriver
>> > >       NetFilterDirection
>> > >       NetLegacy
>> > >       NetLegacyNicOptions
>> > >       NetLegacyOptions
>> > >       NetLegacyOptionsKind
>> > >       Netdev
4>> > >       NetdevBridgeOptions
>> > >       NetdevDumpOptions
>> > >       NetdevHubPortOptions
>> > >       NetdevL2TPv3Options
>> > >       NetdevNetmapOptions
>> > >       NetdevNoneOptions
>> > >       NetdevSocketOptions
>> > >       NetdevTapOptions
>> > >       NetdevUserOptions
>> > >       NetdevVdeOptions
>> > >       NetdevVhostUserOptions
>> > >       NumaNodeOptions
>> > >       NumaOptions
>> > >       NumaOptionsKind
>> > >       OnOffAuto
>> > >       OnOffSplit
>> > >       PreallocMode
>> > >       QCryptoBlockCreateOptions
>> > >       QCryptoBlockCreateOptionsLUKS
>> > >       QCryptoBlockFormat
>> > >       QCryptoBlockInfo
>> > >       QCryptoBlockInfoBase
>> > >       QCryptoBlockInfoQCow
>> > >       QCryptoBlockOpenOptions
>> > >       QCryptoBlockOptionsBase
>> > >       QCryptoBlockOptionsLUKS
>> > >       QCryptoBlockOptionsQCow
>> > >       QCryptoSecretFormat
>> > >       QCryptoTLSCredsEndpoint
>> > >       QapiErrorClass
>> > >       ReplayMode
>> > >       X86CPUFeatureWordInfo
>> > >       X86CPURegister32
>> > >
>> > >   Generating documentation for internal types might be useful, but
>> > >   letting them pollute QMP interface documentation isn't.  Needs fixing
>> > >   before we release.  Until then, needs a FIXME comment in qapi2texi.py.
>> > >
>> > > * Union support is lacking
>> > >
>> > >   The doc string language is adequate for documenting commands, events,
>> > >   and non-union types.  It doesn't really handle union variants. Hardly
>> > >   surprising, as you fitted the language do existing practice, and
>> > >   existing (mal-)practice is neglecting to document union variant
>> > >   members.
>> > >
>> > > * Documentation is lacking
>> > >
>> > >   See review of qapi-code-gen.txt below.
>> > >
>> > > * Doc comment error message positions are imprecise
>> > >
>> > >   They always point to the beginning of the comment.
>> > >
>> > > * Probably more
>> > >
>> > >   We should update this with noteworthy findings during review.  I
>> > >   tried, but I suspect I missed some.
>> >
>> > ok
>> >
>> > >> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
[...]
>> > >> diff --git a/scripts/qapi.py b/scripts/qapi.py
>> > >> index 4d1b0e4..1b456b4 100644
>> > >> --- a/scripts/qapi.py
>> > >> +++ b/scripts/qapi.py
[...]
>> > >> +    def append(self, line):
>> > >> +        """Adds a # comment line, to be parsed and added to current section"""
>> > >
>> > > Imperative mood:
>> > >
>> > >     """Add a comment line, to be parsed and added to the current section."""
>> > >
>> > > However, we're not always adding to the current section, we can also
>> > > start a new one.  The following avoids suggesting anything about the
>> > > current section:
>> > >
>> > >     """Parse a comment line and add it to the documentation."""
>> > >
>> > > When the function name is a verb, and its doc string starts with a
>> > > different verb, the name might be suboptimal.  Use your judgement.
>> > >
>> > > If this one-liner is too terse, we can try a multi-line doc string:
>> > >
>> > >     """Parse a comment line and add it to the documentation.
>> > >
>> > >     TODO should we tell more about how we parse?
>> > >
>> > >     Args:
>> > >         line: the comment starting with '#', with the newline stripped.
>> > >
>> > >     Raises:
>> > >         QAPISchemaError: TODO explain error conditions
>> > >     """
>> > >
>> > > Format stolen from the Google Python Style Guide[2], because PEP257 is
>> > > of not much help there.
>> > >
>> > > Once you start with such elaborate doc strings, pressure will likely
>> > > mount to do the same elsewhere.  Could be a distraction right now.  Use
>> > > your judgement.
>> >
>> > ok, thanks
>> >
>> > >> +        line = line[1:]
>> > >> +        if not line:
>> > >> +            self._append_freeform(line)
>> > >> +            return
>> > >> +
>> > >> +        if line[0] != ' ':
>> > >> +            raise QAPISchemaError(self.parser, "missing space after #")
>> > >> +        line = line[1:]
>> > >
>> > > QAPISchemaError takes the error position from its QAPISchemaParser
>> > > argument.  In other words, the error position depends on the state of
>> > > the parser when it calls this function.  Turns out the position is the
>> > > beginning of the comment.  Servicable here.  Action at a distance,
>> > > though.
>> > >
>> > > Less strict:
>> > >
>> > >            # strip leading # and space
>> > >            line = line[1:]
>> > >            if line[0] == ' ':
>> > >                line = line[1:]
>> > >
>> > > Also avoids special-casing empty comments.
>> >
>> > That would raise "IndexError: string index out of range" on empty comment
>> > lines ("#")
>>
>> Fixable:
>>                if line.startswith(' '):
>>
>> > Also I'd like to keep the error in case a space is missing (it caught some
>> > already).
>>
>> On the one hand, I share your distaste for lack of space between # and
>> comment text.  On the other hand, I dislike enforcing spacing
>> inconsistently: enforce in doc comments, but not in other comments.
>
> It's only enforced for doc comments, which I think is fair.

If it's worth enforcing for some comments (which I doubt), then why
isn't it worth enforcing for all of them?  Anyway, it's something we can
fiddle with on top.

[...]
>> > > Not fixed since v3:
>> > >
>> > > * Flat union where 'base' is a string, e.g. union UserDefFlatUnion in
>> > >   qapi-schema-test.json has base 'UserDefUnionBase', args is set(['a',
>> > >   'B', 'e', 'D', 'f', 'i', 'o', 'n', 's', 'r', 'U'])
>> > >
>> > > * Command where 'data' is a string, e.g. user_def_cmd0 in
>> > >   qapi-schema-test.json has data 'Empty2', args is set(['E', 'm', 'p',
>> > >   '2', 't', 'y'])
>> > >
>> > > * Event where 'data' is a string, no test case handy (hole in test
>> > >   coverage)
>> >
>> > ok, I changed it that way, that fixes it:
>> > +    if isinstance(data, list):
>> > +        args = set([name.strip('*') for name in data])
>> > +    else:
>> > +        args = set()
>>
>> Uh, sure this does the right thing when data is a dict?
>>
>
> yes, the line above takes care of extracting the keys in data.

Ah, I misunderstood.  First mapping dict to list of keys, then dealing
with either list or other should indeed work.

>> > >> +    if meta == 'alternate' or \
>> > >> +       (meta == 'union' and not expr.get('discriminator')):
>> > >> +        args.add('type')
>> > >
>> > > As explained in review of v3, this is only a subset of the real set of
>> > > members.  Computing the exact set is impractical when working with the
>> > > abstract syntax tree.  I believe we'll eventually have to rewrite this
>> > > code to work with the QAPISchemaEntity instead.
>> >
>> > I don't think we want to list all the members as this would lead to
>> > duplicated documentation. Instead it should document only the members of
>> > the expr being defined.
>>
>> You're sticking to established practice, which makes lots of sense.
>> However, established practice is a lot more clear for simple cases than
>> for things like members inherited from base types, variant members and
>> such.  At some point, we'll have to figure out how we want not so simple
>> cases documented.  Until then, we can't really decide how to check
>> documentation completeness.
>>
>> >                         In which case, it looks like this check is good
>> > enough, no?
>>
>> For now, yes.  Later on, maybe.
>>
>> Let's document the limitation in a comment and the commit message, and
>> move on.
>
> I'd appreciate your help to list the limitations in the commit message, as
> I have not as thorough understanding as you, and even worse I often don't
> use the same name for the various concepts.

I'll try.

Deducing concept names from code isn't always easy...

[...]
>> > >> diff --git a/scripts/qapi2texi.py b/scripts/qapi2texi.py
>> > >> new file mode 100755
>> > >> index 0000000..0cec43a
>> > >> --- /dev/null
>> > >> +++ b/scripts/qapi2texi.py
[...]
>> > >> +def texi_union(expr, doc):
>> > >> +    """
>> > >> +    Format an union to texi
>> > >
>> > > I think it's "a union".
>> >
>> > ok
>> >
>> > >> +    """
>> > >> +    attrs = "@{ " + texi_args(expr, "base") + " @}"
>> > >> +    discriminator = expr.get("discriminator")
>> > >> +    if not discriminator:
>> > >> +        union = "Flat Union"
>> > >> +        discriminator = "type"
>> > >> +    else:
>> > >> +        union = "Union"
>> > >
>> > > Condition is backwards.
>> >
>> > fixed
>> >
>> > >> +    attrs += " + '%s' = [ " % discriminator
>> > >> +    attrs += texi_args(expr, "data") + " ]"
>> > >> +    body = texi_body(doc)
>> > >> +
>> > >> +    return STRUCT_FMT(type=union,
>> > >> +                      name=doc.symbol,
>> > >> +                      attrs=attrs,
>> > >> +                      body=body)
>> > >
>> > > You're inventing syntax here.  Example output:
>> > >
>> > >  -- Union: QCryptoBlockOpenOptions { QCryptoBlockOptionsBase } +
>> > >           'format' = [ 'qcow': QCRYPTOBLOCKOPTIONSQCOW, 'luks':
>> > >           QCRYPTOBLOCKOPTIONSLUKS ]
>> > >
>> > >      The options that are available for all encryption formats when
>> > >      opening an existing volume
>> > >           Since: 2.6
>> > >
>> > >  -- Flat Union: ImageInfoSpecific { } + 'type' = [ 'qcow2':
>> > >           IMAGEINFOSPECIFICQCOW2, 'vmdk': IMAGEINFOSPECIFICVMDK, 'luks':
>> > >           QCRYPTOBLOCKINFOLUKS ]
>> > >
>> > >      A discriminated record of image format specific information
>> > >      structures.
>> > >           Since: 1.7
>> > >
>> > > Note that QCryptoBlockOpenOptions is actually a flat union, and
>> > > ImageInfoSpecific a simple union.  As I said, the condition is
>> > > backwards.
>> > >
>> > > The meaning of the attrs part is unobvious.  Familiarity with schema
>> > > syntax doesn't really help.
>> > >
>> > > Could we simply use schema syntax here?
>> >
>> > Union: QCryptoBlockOpenOptions {
>> > 'base': QCryptoBlockOptionsBase,
>> > 'discriminator': 'format',
>> > 'data':  { 'qcow': QCryptoBlockOptionsQCow, 'luks':
>> > QCryptoBlockCreateOptionsLUKS }
>> >
>> > }
>> >
>> > Doesn't look obvious either and pollute the documentation with
>> > schema-specific parameters.
>>
>> The schema syntax for flat unions is pretty horrid :)  I hope to improve
>> it, but there's more urgent fish to fry.
>>
>> > > If not: whatever format you use, you first need to explain it.
>> >
>> > I am not sure my solution is the best and will remain, but ok let's try to
>> > document it for now.
>>
>> Before you pour time into documenting what you have, we should probably
>> discuss what we need.
>>
>
> Can this be marked as limitation and improved laster? The series is big,
> patch is getting bigger over time, it's quite hard to handle all your
> requirements in one go.

I'm sure it is!

How about this: you split off the code to derive documentation from the
schema expressions into its own patch.  The initial qapi2texi patch gets
a FIXME comment instead, explaining what kind of information the
generated documentation lacks.  I think it's basically member types.
The split-off patch then resolves that FIXME.

This lets me apply the first patch.  We can then discuss how to do the
second patch better.  If you're out of time or energy on this topic (I'd
understand), you could even leave it to me.  After all, it's me who's
particular about it...

[...]

^ permalink raw reply	[flat|nested] 48+ messages in thread

end of thread, other threads:[~2016-12-07 16:06 UTC | newest]

Thread overview: 48+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-11-17 15:54 [Qemu-devel] [PATCH v5 00/17] qapi doc generation (whole version, squashed) Marc-André Lureau
2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 01/17] qapi: improve device_add schema Marc-André Lureau
2016-11-17 17:38   ` Markus Armbruster
2016-11-17 19:49   ` Eric Blake
2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 02/17] qga/schema: fix double-return in doc Marc-André Lureau
2016-11-17 17:38   ` Markus Armbruster
2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 03/17] qga/schema: improve guest-set-vcpus Returns: section Marc-André Lureau
2016-11-17 17:39   ` Markus Armbruster
2016-11-18  8:49     ` Marc-André Lureau
2016-11-18 14:07       ` Markus Armbruster
2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 04/17] qapi: fix schema symbol sections Marc-André Lureau
2016-11-17 17:39   ` Markus Armbruster
2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 05/17] qapi: fix missing symbol @prefix Marc-André Lureau
2016-11-17 17:40   ` Markus Armbruster
2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 06/17] qapi: fix various symbols mismatch in documentation Marc-André Lureau
2016-11-17 17:40   ` Markus Armbruster
2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 07/17] qapi: use one symbol per line Marc-André Lureau
2016-11-17 17:40   ` Markus Armbruster
2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 08/17] qapi: add missing colon-ending for section name Marc-André Lureau
2016-11-17 17:41   ` Markus Armbruster
2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 09/17] qapi: add some sections in docs Marc-André Lureau
2016-11-17 17:43   ` Markus Armbruster
2016-11-30 15:38   ` Markus Armbruster
2016-11-30 16:07     ` Marc-André Lureau
2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 10/17] qapi: improve TransactionAction doc Marc-André Lureau
2016-11-17 18:03   ` Markus Armbruster
2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 11/17] docs: add master qapi texi files Marc-André Lureau
2016-11-18  9:09   ` Markus Armbruster
2016-11-17 15:54 ` [Qemu-devel] [PATCH v5 12/17] qapi: rename QAPIExprError/QAPILineError Marc-André Lureau
2016-11-18 10:17   ` Markus Armbruster
2016-11-18 10:31     ` Marc-André Lureau
2016-11-18 14:13       ` Markus Armbruster
2016-11-17 15:55 ` [Qemu-devel] [PATCH v5 13/17] qapi: add qapi2texi script Marc-André Lureau
2016-11-30 16:06   ` Markus Armbruster
2016-12-05 17:35     ` Marc-André Lureau
2016-12-06 11:50       ` Markus Armbruster
2016-12-06 13:07         ` Marc-André Lureau
2016-12-07 16:05           ` Markus Armbruster
2016-11-17 15:55 ` [Qemu-devel] [PATCH v5 14/17] texi2pod: learn quotation, deftp and deftypefn Marc-André Lureau
2016-11-17 15:55 ` [Qemu-devel] [PATCH v5 15/17] (SQUASHED) move doc to schema Marc-André Lureau
2016-11-17 15:55 ` [Qemu-devel] [PATCH v5 16/17] docs: add qemu logo Marc-André Lureau
2016-11-18 12:52   ` Markus Armbruster
2016-11-21 10:50     ` Marc-André Lureau
2016-11-21 12:07       ` Markus Armbruster
2016-11-17 15:55 ` [Qemu-devel] [PATCH v5 17/17] build-sys: add qapi doc generation targets Marc-André Lureau
2016-11-18 12:31   ` Markus Armbruster
2016-11-21 12:30     ` Marc-André Lureau
2016-12-05 16:53 ` [Qemu-devel] [PATCH v5 00/17] qapi doc generation (whole version, squashed) Markus Armbruster

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.