All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v5 00/20] Convert QAPI doc comments to generate rST instead of texinfo
@ 2020-08-10 19:49 Peter Maydell
  2020-08-10 19:50 ` [PATCH v5 01/20] qapi/migration.json: Fix indentation Peter Maydell
                   ` (20 more replies)
  0 siblings, 21 replies; 62+ messages in thread
From: Peter Maydell @ 2020-08-10 19:49 UTC (permalink / raw)
  To: qemu-devel; +Cc: Markus Armbruster

This series switches all our QAPI doc comments over from
texinfo format to rST. It then removes all the texinfo
machinery, because this was the last user of texinfo.

This is largely just a rebase of patchset v4 to current master.
I've had to add three new patches at the front which fix
up newly added QAPI doc comment deviations from the
tightened-up indent rules that the rST conversion requires.
In a couple of places where Markus had already reviewed
patches with a 'reviewed-by with these minor commit message
fixes' I've made the fixes and added his r-by tag.

I've sent this out to the mailing list since I've done the
rebase-work anyway and it seems more useful to review this
than v4. I haven't bothered to re-generate the "here's what
the rendered HTML looks like" since v4, though I can do that
if useful.

Also available as a git branch at
https://git.linaro.org/people/peter.maydell/qemu-arm.git sphinx-conversions-v5

thanks
-- PMM


Below is the same old text from the v3 cover letter, just
for convenience:

The basic approach is somewhat similar to how we deal with kerneldoc
and hxtool: we have a custom Sphinx extension which is passed a
filename which is the json file it should run the QAPI parser over and
generate documentation for. Unlike 'kerneldoc' but somewhat like
hxtool, I have chosed to generate documentation by generating a tree
of docutils nodes, rather than by generating rST source that is then
fed to the rST parser to generate docutils nodes.  Individual lumps of
doc comment go to the rST parser, but the structured parts we render
directly. This makes it easier to get the structure and heading level
nesting correct.

Rather than trying to exactly handle all the existing comments I have
opted (as Markus suggested) to tweak them where this seemed more
sensible than contorting the rST generator to deal with
weirdnesses. The principal changes are:
 * whitespace is now significant, and multiline definitions must have
   their second and subsequent lines indented to match the first line
 * general rST format markup is permitted, not just the small set of
   markup the old texinfo generator handled. For most things (notably
   bulleted and itemized lists) the old format is the same as rST was.
 * Specific things that might trip people up:
   - instead of *bold* and _italic_ rST has **bold** and *italic*
   - lists need a preceding and following blank line
   - a lone literal '*' will need to be backslash-escaped to
     avoid a rST syntax error
 * the old leading '|' for example (literal text) blocks is replaced
   by the standard rST '::' literal block.
 * headings and subheadings must now be in a freeform documentation
   comment of their own
 * we support arbitrary levels of sub- and sub-sub-heading, not just a
   main and sub-heading like the old texinfo generator
 * as a special case, @foo is retained and is equivalent to ``foo``
Moving on to the actual code changes:
 * we start by changing the existing parser code to be more careful
   with leading whitespace: instead of stripping it all, it strips
   only the amount required for indented multiline definitions, and
   complains if it finds an unexpected de-indent. The texinfo
   generator code is updated to do whitespace stripping itself, so
   there is no change to the generated texi source.
 * then we add the new qapidoc Sphinx extension, which is not yet used
   by anything. This is a 500 line script, all added in one patch. I
   can split it if people think that would help, but I'm not sure I
   see a good split point.
 * then we can convert the two generated reference documents, one at a
   time. This is mostly just updating makefile rules and the like.
 * after that we can do some minor tweaks to doc comments that would
   have confused the texinfo parser: changing our two instances of
   '|'-markup literal blocks to rST '::' literal blocks, and adding
   some headings to the GA reference so the rST interop manual ToC
   looks better.
 * finally, we can delete the old texinfo machinery and update the
   markup docs in docs/devel/qapi-code-gen.txt

On headings:

Because the rST generator works by assembling a tree of docutils
nodes, it needs to know the structure of the document, in the
sense that it wants to know that there is a "section with a level
1 heading titled Foo", which contains "section with a level 2
heading titled Bar", which in turn contains the documentation for
commands Baz, Boz, Buz. This means we can't follow the texinfo
generator's approach of just treating '= Foo' as another kind
of markup to be turned into a '@section' texinfo and otherwise
just written out into the output stream. Instead we need to
be able to distinguish "this is a level 1 section heading"
from any other kind of doc-comment, and the user shouldn't be
able to insert directives specifying changes in the document
structure randomly in the middle of what would otherwise be a
lump of "just rST source to be fed to a rST parser".
The approach I've taken to letting the generator know the structure
is to special-case headings into "must be in their own freeform
doc-comment as a single line", like this:
 ##
 # = Foo
 ##
This is easy to spot in the 'freeform' method, and matches how
we already mark up headings in almost all cases. An alternative
approach would be to have parser.py detect heading markup, so
that instead of
        for doc in schema.docs:
            if doc.symbol:
                vis.symbol(doc, schema.lookup_entity(doc.symbol))
            else:
                vis.freeform(doc)
(ie "everything the parser gives you is either documenting
a symbol, or it is a freefrom comment") we have:
        for doc in schema.docs:
            if doc.symbol:
                vis.symbol(doc, schema.lookup_entity(doc.symbol))
            else if doc.is_section_header:
                vis.new_section(doc.heading_text, doc.heading_level)
            else:
                vis.freeform(doc)
(ie "everything the parser gives you is either documenting
a symbol, or a notification about the structure of the document,
or a freeform comment".) I feel that would be less simple than
we currently have, though.

There are a few things I have left out of this initial series:
 * unlike the texinfo, there is no generation of index entries
   or an index in the HTML docs
 * although there are HTML anchors on all the command/object/etc
   headings, they are not stable but just serial-number based
   tags like '#qapidoc-35', so not suitable for trying to link
   to from other parts of the docs
 * unlike the old texinfo generation, we make no attempt to regression
   test the rST generation in 'make check'. This is trickier than
   the texinfo equivalent, because we never generate rST source
   that we could compare against a golden reference. Comparing
   against generated HTML is liable to break with new Sphinx
   versions; trying to compare the data structure of docutils nodes
   would be a bit more robust but would require a bunch of code to
   mock up running Sphinx somehow.

My view is that we can add niceties like this later; the series
already seems big enough to me.




Peter Maydell (20):
  qapi/migration.json: Fix indentation
  qapi: Fix indentation, again
  qapi/block-core.json: Fix nbd-server-start docs
  qapi/qapi-schema.json: Put headers in their own doc-comment blocks
  qapi/machine.json: Escape a literal '*' in doc comment
  tests/qapi/doc-good.json: Prepare for qapi-doc Sphinx extension
  scripts/qapi: Move doc-comment whitespace stripping to doc.py
  scripts/qapi/parser.py: improve doc comment indent handling
  docs/sphinx: Add new qapi-doc Sphinx extension
  docs/interop: Convert qemu-ga-ref to rST
  docs/interop: Convert qemu-qmp-ref to rST
  qapi: Use rST markup for literal blocks
  qga/qapi-schema.json: Add some headings
  scripts/qapi: Remove texinfo generation support
  docs/devel/qapi-code-gen.txt: Update to new rST backend conventions
  Makefile: Remove redundant Texinfo related rules
  scripts/texi2pod: Delete unused script
  Remove Texinfo related files from .gitignore and git.orderfile
  configure: Drop texinfo requirement
  Remove texinfo dependency from docker and CI configs

 docs/conf.py                               |   6 +-
 docs/devel/qapi-code-gen.txt               |  90 ++--
 docs/index.html.in                         |   2 -
 docs/interop/conf.py                       |   4 +
 docs/interop/index.rst                     |   2 +
 docs/interop/qemu-ga-ref.rst               |   4 +
 docs/interop/qemu-ga-ref.texi              |  80 ---
 docs/interop/qemu-qmp-ref.rst              |   4 +
 docs/interop/qemu-qmp-ref.texi             |  80 ---
 docs/sphinx/qapidoc.py                     | 504 +++++++++++++++++++
 configure                                  |  12 +-
 Makefile                                   |  86 +---
 rules.mak                                  |  14 +-
 qapi/audio.json                            |  12 +-
 qapi/block-core.json                       |  30 +-
 qapi/control.json                          |   4 +-
 qapi/machine.json                          |   8 +-
 qapi/migration.json                        |  68 +--
 qapi/misc.json                             |   4 +-
 qapi/net.json                              |   2 +-
 qapi/qapi-schema.json                      |  18 +-
 qga/qapi-schema.json                       |  12 +-
 .gitignore                                 |  15 -
 .travis.yml                                |   1 -
 MAINTAINERS                                |   3 +-
 scripts/checkpatch.pl                      |   2 +-
 scripts/git.orderfile                      |   1 -
 scripts/qapi-gen.py                        |   2 -
 scripts/qapi/doc.py                        | 301 ------------
 scripts/qapi/gen.py                        |   7 -
 scripts/qapi/parser.py                     |  93 +++-
 scripts/texi2pod.pl                        | 536 ---------------------
 tests/Makefile.include                     |  15 +-
 tests/docker/dockerfiles/debian10.docker   |   1 -
 tests/docker/dockerfiles/debian9.docker    |   1 +
 tests/docker/dockerfiles/fedora.docker     |   1 -
 tests/docker/dockerfiles/ubuntu.docker     |   1 -
 tests/docker/dockerfiles/ubuntu1804.docker |   1 -
 tests/docker/dockerfiles/ubuntu2004.docker |   1 -
 tests/qapi-schema/doc-good.json            |  25 +-
 tests/qapi-schema/doc-good.out             |  22 +-
 tests/qapi-schema/doc-good.texi            | 319 ------------
 42 files changed, 784 insertions(+), 1610 deletions(-)
 create mode 100644 docs/interop/qemu-ga-ref.rst
 delete mode 100644 docs/interop/qemu-ga-ref.texi
 create mode 100644 docs/interop/qemu-qmp-ref.rst
 delete mode 100644 docs/interop/qemu-qmp-ref.texi
 create mode 100644 docs/sphinx/qapidoc.py
 delete mode 100644 scripts/qapi/doc.py
 delete mode 100755 scripts/texi2pod.pl
 delete mode 100644 tests/qapi-schema/doc-good.texi

-- 
2.20.1



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

* [PATCH v5 01/20] qapi/migration.json: Fix indentation
  2020-08-10 19:49 [PATCH v5 00/20] Convert QAPI doc comments to generate rST instead of texinfo Peter Maydell
@ 2020-08-10 19:50 ` Peter Maydell
  2020-08-10 19:50 ` [PATCH v5 02/20] qapi: Fix indentation, again Peter Maydell
                   ` (19 subsequent siblings)
  20 siblings, 0 replies; 62+ messages in thread
From: Peter Maydell @ 2020-08-10 19:50 UTC (permalink / raw)
  To: qemu-devel; +Cc: Markus Armbruster

Commits 6a9ad1542065ca0bd54c6 and 9004db48c080632aef23 added some
new text to qapi/migration.json which doesn't fit the stricter
indentation requirements imposed by the rST documentation generator.
Reindent those lines to the new standard.

Reviewed-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 qapi/migration.json | 60 ++++++++++++++++++++++-----------------------
 1 file changed, 30 insertions(+), 30 deletions(-)

diff --git a/qapi/migration.json b/qapi/migration.json
index ea53b23dca9..e07c9c0f1aa 100644
--- a/qapi/migration.json
+++ b/qapi/migration.json
@@ -629,18 +629,18 @@
 #                       Defaults to none. (Since 5.0)
 #
 # @multifd-zlib-level: Set the compression level to be used in live
-#          migration, the compression level is an integer between 0
-#          and 9, where 0 means no compression, 1 means the best
-#          compression speed, and 9 means best compression ratio which
-#          will consume more CPU.
-#          Defaults to 1. (Since 5.0)
+#                      migration, the compression level is an integer between 0
+#                      and 9, where 0 means no compression, 1 means the best
+#                      compression speed, and 9 means best compression ratio which
+#                      will consume more CPU.
+#                      Defaults to 1. (Since 5.0)
 #
 # @multifd-zstd-level: Set the compression level to be used in live
-#          migration, the compression level is an integer between 0
-#          and 20, where 0 means no compression, 1 means the best
-#          compression speed, and 20 means best compression ratio which
-#          will consume more CPU.
-#          Defaults to 1. (Since 5.0)
+#                      migration, the compression level is an integer between 0
+#                      and 20, where 0 means no compression, 1 means the best
+#                      compression speed, and 20 means best compression ratio which
+#                      will consume more CPU.
+#                      Defaults to 1. (Since 5.0)
 #
 # Since: 2.4
 ##
@@ -769,18 +769,18 @@
 #                       Defaults to none. (Since 5.0)
 #
 # @multifd-zlib-level: Set the compression level to be used in live
-#          migration, the compression level is an integer between 0
-#          and 9, where 0 means no compression, 1 means the best
-#          compression speed, and 9 means best compression ratio which
-#          will consume more CPU.
-#          Defaults to 1. (Since 5.0)
+#                      migration, the compression level is an integer between 0
+#                      and 9, where 0 means no compression, 1 means the best
+#                      compression speed, and 9 means best compression ratio which
+#                      will consume more CPU.
+#                      Defaults to 1. (Since 5.0)
 #
 # @multifd-zstd-level: Set the compression level to be used in live
-#          migration, the compression level is an integer between 0
-#          and 20, where 0 means no compression, 1 means the best
-#          compression speed, and 20 means best compression ratio which
-#          will consume more CPU.
-#          Defaults to 1. (Since 5.0)
+#                      migration, the compression level is an integer between 0
+#                      and 20, where 0 means no compression, 1 means the best
+#                      compression speed, and 20 means best compression ratio which
+#                      will consume more CPU.
+#                      Defaults to 1. (Since 5.0)
 #
 # Since: 2.4
 ##
@@ -945,18 +945,18 @@
 #                       Defaults to none. (Since 5.0)
 #
 # @multifd-zlib-level: Set the compression level to be used in live
-#          migration, the compression level is an integer between 0
-#          and 9, where 0 means no compression, 1 means the best
-#          compression speed, and 9 means best compression ratio which
-#          will consume more CPU.
-#          Defaults to 1. (Since 5.0)
+#                      migration, the compression level is an integer between 0
+#                      and 9, where 0 means no compression, 1 means the best
+#                      compression speed, and 9 means best compression ratio which
+#                      will consume more CPU.
+#                      Defaults to 1. (Since 5.0)
 #
 # @multifd-zstd-level: Set the compression level to be used in live
-#          migration, the compression level is an integer between 0
-#          and 20, where 0 means no compression, 1 means the best
-#          compression speed, and 20 means best compression ratio which
-#          will consume more CPU.
-#          Defaults to 1. (Since 5.0)
+#                      migration, the compression level is an integer between 0
+#                      and 20, where 0 means no compression, 1 means the best
+#                      compression speed, and 20 means best compression ratio which
+#                      will consume more CPU.
+#                      Defaults to 1. (Since 5.0)
 #
 # Since: 2.4
 ##
-- 
2.20.1



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

* [PATCH v5 02/20] qapi: Fix indentation, again
  2020-08-10 19:49 [PATCH v5 00/20] Convert QAPI doc comments to generate rST instead of texinfo Peter Maydell
  2020-08-10 19:50 ` [PATCH v5 01/20] qapi/migration.json: Fix indentation Peter Maydell
@ 2020-08-10 19:50 ` Peter Maydell
  2020-08-14 18:39   ` Richard Henderson
  2020-08-10 19:50 ` [PATCH v5 03/20] qapi/block-core.json: Fix nbd-server-start docs Peter Maydell
                   ` (18 subsequent siblings)
  20 siblings, 1 reply; 62+ messages in thread
From: Peter Maydell @ 2020-08-10 19:50 UTC (permalink / raw)
  To: qemu-devel; +Cc: Markus Armbruster

In commit 26ec4e53f2 and similar commits we fixed the indentation
for doc comments in our qapi json files to follow a new stricter
standard for indentation, which permits only:
    @arg: description line 1
          description line 2

or:
    @arg:
    line 1
    line 2

Unfortunately since we didn't manage to get the script changes that
enforced the new style in, a variety of commits (eg df4097aeaf71,
2e4457032105) introduced new doc text which doesn't follow the new
stricter rules for indentation on multi-line doc comments.  Bring
those into line with the new rules.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 qapi/audio.json      | 12 ++++++------
 qapi/block-core.json |  8 ++++----
 qapi/control.json    |  4 ++--
 qapi/machine.json    |  6 +++---
 qapi/migration.json  |  8 ++++----
 qapi/misc.json       |  4 ++--
 qapi/net.json        |  2 +-
 7 files changed, 22 insertions(+), 22 deletions(-)

diff --git a/qapi/audio.json b/qapi/audio.json
index f62bd0d7f6e..3b843878d23 100644
--- a/qapi/audio.json
+++ b/qapi/audio.json
@@ -159,20 +159,20 @@
 # recording.
 #
 # @server-name: select from among several possible concurrent server instances
-# (default: environment variable $JACK_DEFAULT_SERVER if set, else "default")
+#               (default: environment variable $JACK_DEFAULT_SERVER if set, else "default")
 #
 # @client-name: the client name to use. The server will modify this name to
-# create a unique variant, if needed unless @exact-name is true (default: the
-# guest's name)
+#               create a unique variant, if needed unless @exact-name is true (default: the
+#               guest's name)
 #
 # @connect-ports: if set, a regular expression of JACK client port name(s) to
-# monitor for and automatically connect to
+#                 monitor for and automatically connect to
 #
 # @start-server: start a jack server process if one is not already present
-# (default: false)
+#                (default: false)
 #
 # @exact-name: use the exact name requested otherwise JACK automatically
-# generates a unique one, if needed (default: false)
+#              generates a unique one, if needed (default: false)
 #
 # Since: 5.1
 ##
diff --git a/qapi/block-core.json b/qapi/block-core.json
index 197bdc1c36d..f04bb3f0dd5 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -369,7 +369,7 @@
 #
 # Features:
 # @deprecated: Member @encryption_key_missing is deprecated.  It is
-#     always false.
+#              always false.
 #
 # Since: 0.14.0
 #
@@ -504,7 +504,7 @@
 #
 # Features:
 # @deprecated: Member @status is deprecated.  Use @recording and
-#     @locked instead.
+#              @locked instead.
 #
 # Since: 1.3
 ##
@@ -614,7 +614,7 @@
 #
 # Features:
 # @deprecated: Member @dirty-bitmaps is deprecated.  Use @inserted
-#     member @dirty-bitmaps instead.
+#              member @dirty-bitmaps instead.
 #
 # Since:  0.14.0
 ##
@@ -1642,7 +1642,7 @@
 #
 # Features:
 # @deprecated: Members @base and @top are deprecated.  Use @base-node
-#     and @top-node instead.
+#              and @top-node instead.
 #
 # Returns: - Nothing on success
 #          - If @device does not exist, DeviceNotFound
diff --git a/qapi/control.json b/qapi/control.json
index de51e9916c3..134f842bafa 100644
--- a/qapi/control.json
+++ b/qapi/control.json
@@ -177,8 +177,8 @@
 #
 # Features:
 # @deprecated: This command is deprecated, because its output doesn't
-#     reflect compile-time configuration.  Use 'query-qmp-schema'
-#     instead.
+#              reflect compile-time configuration.  Use 'query-qmp-schema'
+#              instead.
 #
 # Returns: A list of @EventInfo.
 #
diff --git a/qapi/machine.json b/qapi/machine.json
index 481b1f07ecc..5aee2bbd169 100644
--- a/qapi/machine.json
+++ b/qapi/machine.json
@@ -191,8 +191,8 @@
 #
 # Features:
 # @deprecated: This command is deprecated, because it interferes with
-#     the guest.  Use 'query-cpus-fast' instead to avoid the vCPU
-#     interruption.
+#              the guest.  Use 'query-cpus-fast' instead to avoid the vCPU
+#              interruption.
 #
 # Returns: a list of @CpuInfo for each virtual CPU
 #
@@ -316,7 +316,7 @@
 #
 # Features:
 # @deprecated: This command is deprecated.  Use `device_add` instead.
-#     See the `query-hotpluggable-cpus` command for details.
+#              See the `query-hotpluggable-cpus` command for details.
 #
 # Returns: Nothing on success
 #
diff --git a/qapi/migration.json b/qapi/migration.json
index e07c9c0f1aa..66a6fbb89d8 100644
--- a/qapi/migration.json
+++ b/qapi/migration.json
@@ -1264,7 +1264,7 @@
 #
 # Features:
 # @deprecated: This command is deprecated.  Use
-#     'migrate-set-parameters' instead.
+#              'migrate-set-parameters' instead.
 #
 # Returns: nothing on success
 #
@@ -1288,7 +1288,7 @@
 #
 # Features:
 # @deprecated: This command is deprecated.  Use
-#     'migrate-set-parameters' instead.
+#              'migrate-set-parameters' instead.
 #
 # Returns: nothing on success
 #
@@ -1312,7 +1312,7 @@
 #
 # Features:
 # @deprecated: This command is deprecated.  Use
-#     'migrate-set-parameters' instead.
+#              'migrate-set-parameters' instead.
 #
 # The size will be rounded down to the nearest power of 2.
 # The cache size can be modified before and during ongoing migration
@@ -1338,7 +1338,7 @@
 #
 # Features:
 # @deprecated: This command is deprecated.  Use
-#     'query-migrate-parameters' instead.
+#              'query-migrate-parameters' instead.
 #
 # Returns: XBZRLE cache size in bytes
 #
diff --git a/qapi/misc.json b/qapi/misc.json
index 9d32820dc1b..8cf6ebe67cb 100644
--- a/qapi/misc.json
+++ b/qapi/misc.json
@@ -877,8 +877,8 @@
 #
 # Features:
 # @deprecated: This command is deprecated.  For changing block
-#     devices, use 'blockdev-change-medium' instead; for changing VNC
-#     parameters, use 'change-vnc-password' instead.
+#              devices, use 'blockdev-change-medium' instead; for changing VNC
+#              parameters, use 'change-vnc-password' instead.
 #
 # Returns: - Nothing on success.
 #          - If @device is not a valid block device, DeviceNotFound
diff --git a/qapi/net.json b/qapi/net.json
index ddb113e5e5a..a3a13360018 100644
--- a/qapi/net.json
+++ b/qapi/net.json
@@ -457,7 +457,7 @@
 #
 # Since: 2.7
 #
-# @vhost-vdpa since 5.1
+#        @vhost-vdpa since 5.1
 ##
 { 'enum': 'NetClientDriver',
   'data': [ 'none', 'nic', 'user', 'tap', 'l2tpv3', 'socket', 'vde',
-- 
2.20.1



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

* [PATCH v5 03/20] qapi/block-core.json: Fix nbd-server-start docs
  2020-08-10 19:49 [PATCH v5 00/20] Convert QAPI doc comments to generate rST instead of texinfo Peter Maydell
  2020-08-10 19:50 ` [PATCH v5 01/20] qapi/migration.json: Fix indentation Peter Maydell
  2020-08-10 19:50 ` [PATCH v5 02/20] qapi: Fix indentation, again Peter Maydell
@ 2020-08-10 19:50 ` Peter Maydell
  2020-08-14 18:39   ` Richard Henderson
  2020-08-10 19:50 ` [PATCH v5 04/20] qapi/qapi-schema.json: Put headers in their own doc-comment blocks Peter Maydell
                   ` (17 subsequent siblings)
  20 siblings, 1 reply; 62+ messages in thread
From: Peter Maydell @ 2020-08-10 19:50 UTC (permalink / raw)
  To: qemu-devel; +Cc: Markus Armbruster

Commit eed8b6917832 added some new text to the nbd-server-start
documentation in the wrong place.  Since this is after the 'Returns:'
line it's parsed as if it were part of the documentation of the
"Returns:' information.  Move it up to join the rest of the
"documentation of the type as a whole" doc text.

This doesn't look odd in the current HTML rendering, but the
new QAPI-to-rST handling will complain about the indent level
of the lines not matching up with the 'Returns:' line.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 qapi/block-core.json | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/qapi/block-core.json b/qapi/block-core.json
index f04bb3f0dd5..535b2b2e7bf 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -5206,6 +5206,9 @@
 # server will present them as named exports; for example, another
 # QEMU instance could refer to them as "nbd:HOST:PORT:exportname=NAME".
 #
+# Keep this type consistent with the NbdServerOptions type. The only intended
+# difference is using SocketAddressLegacy instead of SocketAddress.
+#
 # @addr: Address on which to listen.
 # @tls-creds: ID of the TLS credentials object (since 2.6).
 # @tls-authz: ID of the QAuthZ authorization object used to validate
@@ -5216,9 +5219,6 @@
 #
 # Returns: error if the server is already running.
 #
-# Keep this type consistent with the NbdServerOptions type. The only intended
-# difference is using SocketAddressLegacy instead of SocketAddress.
-#
 # Since: 1.3.0
 ##
 { 'command': 'nbd-server-start',
-- 
2.20.1



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

* [PATCH v5 04/20] qapi/qapi-schema.json: Put headers in their own doc-comment blocks
  2020-08-10 19:49 [PATCH v5 00/20] Convert QAPI doc comments to generate rST instead of texinfo Peter Maydell
                   ` (2 preceding siblings ...)
  2020-08-10 19:50 ` [PATCH v5 03/20] qapi/block-core.json: Fix nbd-server-start docs Peter Maydell
@ 2020-08-10 19:50 ` Peter Maydell
  2020-08-10 19:50 ` [PATCH v5 05/20] qapi/machine.json: Escape a literal '*' in doc comment Peter Maydell
                   ` (16 subsequent siblings)
  20 siblings, 0 replies; 62+ messages in thread
From: Peter Maydell @ 2020-08-10 19:50 UTC (permalink / raw)
  To: qemu-devel; +Cc: Markus Armbruster

Our current QAPI doc-comment markup allows section headers
(introduced with a leading '=' or '==') anywhere in any documentation
comment.  This works for texinfo because the texi generator simply
prints a texinfo heading directive at that point in the output
stream.  For rST generation, since we're assembling a tree of
docutils nodes, this is awkward because a new section implies
starting a new section node at the top level of the tree and
generating text into there.

New section headings in the middle of the documentation of a command
or event would be pretty nonsensical, and in fact we only ever output
new headings using 'freeform' doc comment blocks whose only content
is the single line of the heading, with two exceptions, which are in
the introductory freeform-doc-block at the top of
qapi/qapi-schema.json.

Split that doc-comment up so that the heading lines are in their own
doc-comment.  This will allow us to tighten the specification to
insist that heading lines are always standalone, rather than
requiring the rST document generator to look at every line in a doc
comment block and handle headings in odd places.

This change makes no difference to the generated texi.

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 qapi/qapi-schema.json | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json
index f03ff91ceb5..5fc0771eb04 100644
--- a/qapi/qapi-schema.json
+++ b/qapi/qapi-schema.json
@@ -2,7 +2,9 @@
 # vim: filetype=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
@@ -26,9 +28,13 @@
 #
 # Please, refer to the QMP specification (docs/interop/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.
-- 
2.20.1



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

* [PATCH v5 05/20] qapi/machine.json: Escape a literal '*' in doc comment
  2020-08-10 19:49 [PATCH v5 00/20] Convert QAPI doc comments to generate rST instead of texinfo Peter Maydell
                   ` (3 preceding siblings ...)
  2020-08-10 19:50 ` [PATCH v5 04/20] qapi/qapi-schema.json: Put headers in their own doc-comment blocks Peter Maydell
@ 2020-08-10 19:50 ` Peter Maydell
  2020-08-10 19:50 ` [PATCH v5 06/20] tests/qapi/doc-good.json: Prepare for qapi-doc Sphinx extension Peter Maydell
                   ` (15 subsequent siblings)
  20 siblings, 0 replies; 62+ messages in thread
From: Peter Maydell @ 2020-08-10 19:50 UTC (permalink / raw)
  To: qemu-devel; +Cc: Markus Armbruster

For rST, '*' is a kind of inline markup (for emphasis), so
"*-softmmu" is a syntax error because of the missing closing '*'.
Escape the '*' with a '\'.

The texinfo document generator will leave the '\' in the
output, which is not ideal, but that generator is going to
go away in a subsequent commit.

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 qapi/machine.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/qapi/machine.json b/qapi/machine.json
index 5aee2bbd169..3f6ea5693d1 100644
--- a/qapi/machine.json
+++ b/qapi/machine.json
@@ -13,7 +13,7 @@
 #
 # The comprehensive enumeration of QEMU system emulation ("softmmu")
 # targets. Run "./configure --help" in the project root directory, and
-# look for the *-softmmu targets near the "--target-list" option. The
+# look for the \*-softmmu targets near the "--target-list" option. The
 # individual target constants are not documented here, for the time
 # being.
 #
-- 
2.20.1



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

* [PATCH v5 06/20] tests/qapi/doc-good.json: Prepare for qapi-doc Sphinx extension
  2020-08-10 19:49 [PATCH v5 00/20] Convert QAPI doc comments to generate rST instead of texinfo Peter Maydell
                   ` (4 preceding siblings ...)
  2020-08-10 19:50 ` [PATCH v5 05/20] qapi/machine.json: Escape a literal '*' in doc comment Peter Maydell
@ 2020-08-10 19:50 ` Peter Maydell
  2020-09-04  8:10   ` Markus Armbruster
  2020-08-10 19:50 ` [PATCH v5 07/20] scripts/qapi: Move doc-comment whitespace stripping to doc.py Peter Maydell
                   ` (14 subsequent siblings)
  20 siblings, 1 reply; 62+ messages in thread
From: Peter Maydell @ 2020-08-10 19:50 UTC (permalink / raw)
  To: qemu-devel; +Cc: Markus Armbruster

doc-good.json tests doc comment parser corner cases.  We're about to
largely replace it by a Sphinx extension, which will have different
corner cases.  Tweak the test so it passes both with the old parser
and the Sphinx extension, by making it match the more restrictive
rST syntax:

 * in a single list the bullet types must all match
 * lists must have leading and following blank lines
 * indentation is important
 * the '|' example syntax is going to go away entirely, so stop
   testing it

This will avoid the tests spuriously breaking when we tighten up the
parser code in the following commits.

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
Changes v4->v5:
 improved commit message with Markus' suggested text
---
 tests/qapi-schema/doc-good.json | 25 +++++++++++++------------
 tests/qapi-schema/doc-good.out  | 12 ++++++------
 tests/qapi-schema/doc-good.texi | 12 +++---------
 3 files changed, 22 insertions(+), 27 deletions(-)

diff --git a/tests/qapi-schema/doc-good.json b/tests/qapi-schema/doc-good.json
index 9da72a1f556..c6822145c49 100644
--- a/tests/qapi-schema/doc-good.json
+++ b/tests/qapi-schema/doc-good.json
@@ -12,25 +12,25 @@
 #
 # *strong* _with emphasis_
 # @var {in braces}
+#
 # * List item one
-# - Two, multiple
+# * Two, multiple
 #   lines
 #
-# 3. Three
-# Still in list
+# * Three
+#   Still in list
+#
+# Not in list
 #
-#   Not in list
 # - Second list
-# Note: still in list
+#   Note: still in list
 #
 # Note: not in list
+#
 # 1. Third list
 #    is numbered
 #
-# - another item
-#
-# | example
-# | multiple lines
+# 2. another item
 #
 # Returns: the King
 # Since: the first age
@@ -68,7 +68,7 @@
 ##
 # @Base:
 # @base1:
-#   the first member
+# the first member
 ##
 { 'struct': 'Base', 'data': { 'base1': 'Enum' } }
 
@@ -116,7 +116,7 @@
 ##
 # @Alternate:
 # @i: an integer
-# @b is undocumented
+#     @b is undocumented
 #
 # Features:
 # @alt-feat: a feature
@@ -134,7 +134,7 @@
 # @arg1: the first argument
 #
 # @arg2: the second
-# argument
+#        argument
 #
 # Features:
 # @cmd-feat1: a feature
@@ -143,6 +143,7 @@
 # Returns: @Object
 # TODO: frobnicate
 # Notes:
+#
 # - Lorem ipsum dolor sit amet
 # - Ut enim ad minim veniam
 #
diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out
index 6757dd26a2f..4c24eb96486 100644
--- a/tests/qapi-schema/doc-good.out
+++ b/tests/qapi-schema/doc-good.out
@@ -74,25 +74,25 @@ doc freeform
 
 *strong* _with emphasis_
 @var {in braces}
+
 * List item one
-- Two, multiple
+* Two, multiple
 lines
 
-3. Three
+* Three
 Still in list
 
 Not in list
+
 - Second list
 Note: still in list
 
 Note: not in list
+
 1. Third list
 is numbered
 
-- another item
-
-| example
-| multiple lines
+2. another item
 
 Returns: the King
 Since: the first age
diff --git a/tests/qapi-schema/doc-good.texi b/tests/qapi-schema/doc-good.texi
index 7f28fb7a0fb..12808989ffb 100644
--- a/tests/qapi-schema/doc-good.texi
+++ b/tests/qapi-schema/doc-good.texi
@@ -6,6 +6,7 @@
 
 @strong{strong} @emph{with emphasis}
 @code{var} @{in braces@}
+
 @itemize @bullet
 @item
 List item one
@@ -20,6 +21,7 @@ Still in list
 @end itemize
 
 Not in list
+
 @itemize @minus
 @item
 Second list
@@ -28,6 +30,7 @@ Note: still in list
 @end itemize
 
 Note: not in list
+
 @enumerate
 @item
 Third list
@@ -36,15 +39,6 @@ is numbered
 @item
 another item
 
-@example
-example
-@end example
-
-@example
-multiple lines
-@end example
-
-
 @end enumerate
 
 Returns: the King
-- 
2.20.1



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

* [PATCH v5 07/20] scripts/qapi: Move doc-comment whitespace stripping to doc.py
  2020-08-10 19:49 [PATCH v5 00/20] Convert QAPI doc comments to generate rST instead of texinfo Peter Maydell
                   ` (5 preceding siblings ...)
  2020-08-10 19:50 ` [PATCH v5 06/20] tests/qapi/doc-good.json: Prepare for qapi-doc Sphinx extension Peter Maydell
@ 2020-08-10 19:50 ` Peter Maydell
  2020-08-10 19:50 ` [PATCH v5 08/20] scripts/qapi/parser.py: improve doc comment indent handling Peter Maydell
                   ` (13 subsequent siblings)
  20 siblings, 0 replies; 62+ messages in thread
From: Peter Maydell @ 2020-08-10 19:50 UTC (permalink / raw)
  To: qemu-devel; +Cc: Markus Armbruster

As we accumulate lines from doc comments when parsing the JSON, the
QAPIDoc class generally strips leading and trailing whitespace using
line.strip() when it calls _append_freeform().  This is fine for
Texinfo, but for rST leading whitespace is significant.  We'd like to
move to having the text in doc comments be rST format rather than a
custom syntax, so move the removal of leading whitespace from the
QAPIDoc class to the texinfo-specific processing code in
texi_format() in qapi/doc.py.

(Trailing whitespace will always be stripped by the rstrip() in
Section::append regardless.)

In a followup commit we will make the whitespace in the lines of doc
comment sections more consistently follow the input source.

There is no change to the generated .texi files before and after this
commit.

Because the qapi-schema test checks the exact values of the
documentation comments against a reference, we need to update that
reference to match the new whitespace.  In the first four places this
is now correctly checking that we did put in the amount of whitespace
to pass a rST-formatted list to the backend; in the last two places
the extra whitespace is 'wrong' and will go away again in the
following commit.

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
v4->v5: just fixed the caps in 'Texinfo' in commit message
---
 scripts/qapi/doc.py            |  1 +
 scripts/qapi/parser.py         | 12 ++++--------
 tests/qapi-schema/doc-good.out | 12 ++++++------
 3 files changed, 11 insertions(+), 14 deletions(-)

diff --git a/scripts/qapi/doc.py b/scripts/qapi/doc.py
index 92f584edcf1..7764de1e4bc 100644
--- a/scripts/qapi/doc.py
+++ b/scripts/qapi/doc.py
@@ -79,6 +79,7 @@ def texi_format(doc):
     inlist = ''
     lastempty = False
     for line in doc.split('\n'):
+        line = line.strip()
         empty = line == ''
 
         # FIXME: Doing this in a single if / elif chain is
diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py
index abadacbb0e8..7fae4478d34 100644
--- a/scripts/qapi/parser.py
+++ b/scripts/qapi/parser.py
@@ -417,10 +417,10 @@ class QAPIDoc:
                 self._append_line = self._append_various_line
                 self._append_various_line(line)
             else:
-                self._append_freeform(line.strip())
+                self._append_freeform(line)
         else:
             # This is a free-form documentation block
-            self._append_freeform(line.strip())
+            self._append_freeform(line)
 
     def _append_args_line(self, line):
         """
@@ -453,7 +453,7 @@ class QAPIDoc:
                 self._append_various_line(line)
             return
 
-        self._append_freeform(line.strip())
+        self._append_freeform(line)
 
     def _append_features_line(self, line):
         name = line.split(' ', 1)[0]
@@ -472,7 +472,7 @@ class QAPIDoc:
             self._append_various_line(line)
             return
 
-        self._append_freeform(line.strip())
+        self._append_freeform(line)
 
     def _append_various_line(self, line):
         """
@@ -495,10 +495,6 @@ class QAPIDoc:
             line = line[len(name)+1:]
             self._start_section(name[:-1])
 
-        if (not self._section.name or
-                not self._section.name.startswith('Example')):
-            line = line.strip()
-
         self._append_freeform(line)
 
     def _start_symbol_section(self, symbols_dict, name):
diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out
index 4c24eb96486..0ef85d959ac 100644
--- a/tests/qapi-schema/doc-good.out
+++ b/tests/qapi-schema/doc-good.out
@@ -77,20 +77,20 @@ doc freeform
 
 * List item one
 * Two, multiple
-lines
+  lines
 
 * Three
-Still in list
+  Still in list
 
 Not in list
 
 - Second list
-Note: still in list
+  Note: still in list
 
 Note: not in list
 
 1. Third list
-is numbered
+   is numbered
 
 2. another item
 
@@ -158,7 +158,7 @@ doc symbol=Alternate
 
     arg=i
 an integer
-@b is undocumented
+    @b is undocumented
     arg=b
 
     feature=alt-feat
@@ -173,7 +173,7 @@ doc symbol=cmd
 the first argument
     arg=arg2
 the second
-argument
+       argument
     arg=arg3
 
     feature=cmd-feat1
-- 
2.20.1



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

* [PATCH v5 08/20] scripts/qapi/parser.py: improve doc comment indent handling
  2020-08-10 19:49 [PATCH v5 00/20] Convert QAPI doc comments to generate rST instead of texinfo Peter Maydell
                   ` (6 preceding siblings ...)
  2020-08-10 19:50 ` [PATCH v5 07/20] scripts/qapi: Move doc-comment whitespace stripping to doc.py Peter Maydell
@ 2020-08-10 19:50 ` Peter Maydell
  2020-09-04  9:03   ` Markus Armbruster
  2020-08-10 19:50 ` [PATCH v5 09/20] docs/sphinx: Add new qapi-doc Sphinx extension Peter Maydell
                   ` (12 subsequent siblings)
  20 siblings, 1 reply; 62+ messages in thread
From: Peter Maydell @ 2020-08-10 19:50 UTC (permalink / raw)
  To: qemu-devel; +Cc: Markus Armbruster

Make the handling of indentation in doc comments more sophisticated,
so that when we see a section like:

Notes: some text
       some more text
          indented line 3

we save it for the doc-comment processing code as:

some text
some more text
   indented line 3

and when we see a section with the heading on its own line:

Notes:

some text
some more text
   indented text

we also accept that and save it in the same form.

The exception is that we always retain indentation as-is for Examples
sections, because these are literal text.

If we detect that the comment document text is not indented as much
as we expect it to be, we throw a parse error.  (We don't complain
about over-indented sections, because for rST this can be legitimate
markup.)

The golden reference for the doc comment text is updated to remove
the two 'wrong' indents; these now form a test case that we correctly
stripped leading whitespace from an indented multi-line argument
definition.

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
v1->v2: Update doc-good.out as per final para.
---
 scripts/qapi/parser.py         | 81 +++++++++++++++++++++++++++-------
 tests/qapi-schema/doc-good.out |  4 +-
 2 files changed, 67 insertions(+), 18 deletions(-)

diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py
index 7fae4478d34..d9f11eadd96 100644
--- a/scripts/qapi/parser.py
+++ b/scripts/qapi/parser.py
@@ -308,18 +308,32 @@ class QAPIDoc:
     """
 
     class Section:
-        def __init__(self, name=None):
+        def __init__(self, parser, name=None, indent=0):
+            # parser, for error messages about indentation
+            self._parser = parser
             # optional section name (argument/member or section name)
             self.name = name
             # the list of lines for this section
             self.text = ''
+            # the expected indent level of the text of this section
+            self._indent = indent
 
         def append(self, line):
+            # Strip leading spaces corresponding to the expected indent level
+            # Blank lines are always OK.
+            if line:
+                spacecount = len(line) - len(line.lstrip(" "))
+                if spacecount > self._indent:
+                    spacecount = self._indent
+                if spacecount < self._indent:
+                    raise QAPIParseError(self._parser, "unexpected de-indent")
+                line = line[spacecount:]
+
             self.text += line.rstrip() + '\n'
 
     class ArgSection(Section):
-        def __init__(self, name):
-            super().__init__(name)
+        def __init__(self, parser, name, indent=0):
+            super().__init__(parser, name, indent)
             self.member = None
 
         def connect(self, member):
@@ -333,7 +347,7 @@ class QAPIDoc:
         self._parser = parser
         self.info = info
         self.symbol = None
-        self.body = QAPIDoc.Section()
+        self.body = QAPIDoc.Section(parser)
         # dict mapping parameter name to ArgSection
         self.args = OrderedDict()
         self.features = OrderedDict()
@@ -438,7 +452,18 @@ class QAPIDoc:
 
         if name.startswith('@') and name.endswith(':'):
             line = line[len(name)+1:]
-            self._start_args_section(name[1:-1])
+            if not line or line.isspace():
+                # Line was just the "@arg:" header; following lines
+                # are not indented
+                indent = 0
+                line = ''
+            else:
+                # Line is "@arg: first line of description"; following
+                # lines should be indented by len(name) + 1, and we
+                # pad out this first line so it is handled the same way
+                indent = len(name) + 1
+                line = ' ' * indent + line
+            self._start_args_section(name[1:-1], indent)
         elif self._is_section_tag(name):
             self._append_line = self._append_various_line
             self._append_various_line(line)
@@ -460,7 +485,17 @@ class QAPIDoc:
 
         if name.startswith('@') and name.endswith(':'):
             line = line[len(name)+1:]
-            self._start_features_section(name[1:-1])
+            if not line or line.isspace():
+                # Line is just the "@name:" header, no ident for following lines
+                indent = 0
+                line = ''
+            else:
+                # Line is "@arg: first line of description"; following
+                # lines should be indented by len(name) + 3, and we
+                # pad out this first line so it is handled the same way
+                indent = len(name) + 1
+                line = ' ' * indent + line
+            self._start_features_section(name[1:-1], indent)
         elif self._is_section_tag(name):
             self._append_line = self._append_various_line
             self._append_various_line(line)
@@ -493,11 +528,23 @@ class QAPIDoc:
                                  % (name, self.sections[0].name))
         if self._is_section_tag(name):
             line = line[len(name)+1:]
-            self._start_section(name[:-1])
+            if not line or line.isspace():
+                # Line is just "SectionName:", no indent for following lines
+                indent = 0
+                line = ''
+            elif name.startswith("Example"):
+                # The "Examples" section is literal-text, so preserve
+                # all the indentation as-is
+                indent = 0
+            else:
+                # Line is "SectionName: some text", indent required
+                indent = len(name) + 1
+                line = ' ' * indent + line
+            self._start_section(name[:-1], indent)
 
         self._append_freeform(line)
 
-    def _start_symbol_section(self, symbols_dict, name):
+    def _start_symbol_section(self, symbols_dict, name, indent):
         # FIXME invalid names other than the empty string aren't flagged
         if not name:
             raise QAPIParseError(self._parser, "invalid parameter name")
@@ -506,21 +553,21 @@ class QAPIDoc:
                                  "'%s' parameter name duplicated" % name)
         assert not self.sections
         self._end_section()
-        self._section = QAPIDoc.ArgSection(name)
+        self._section = QAPIDoc.ArgSection(self._parser, name, indent)
         symbols_dict[name] = self._section
 
-    def _start_args_section(self, name):
-        self._start_symbol_section(self.args, name)
+    def _start_args_section(self, name, indent):
+        self._start_symbol_section(self.args, name, indent)
 
-    def _start_features_section(self, name):
-        self._start_symbol_section(self.features, name)
+    def _start_features_section(self, name, indent):
+        self._start_symbol_section(self.features, name, indent)
 
-    def _start_section(self, name=None):
+    def _start_section(self, name=None, indent=0):
         if name in ('Returns', 'Since') and self.has_section(name):
             raise QAPIParseError(self._parser,
                                  "duplicated '%s' section" % name)
         self._end_section()
-        self._section = QAPIDoc.Section(name)
+        self._section = QAPIDoc.Section(self._parser, name, indent)
         self.sections.append(self._section)
 
     def _end_section(self):
@@ -543,7 +590,7 @@ class QAPIDoc:
     def connect_member(self, member):
         if member.name not in self.args:
             # Undocumented TODO outlaw
-            self.args[member.name] = QAPIDoc.ArgSection(member.name)
+            self.args[member.name] = QAPIDoc.ArgSection(self._parser, member.name)
         self.args[member.name].connect(member)
 
     def connect_feature(self, feature):
@@ -551,6 +598,8 @@ class QAPIDoc:
             raise QAPISemError(feature.info,
                                "feature '%s' lacks documentation"
                                % feature.name)
+            self.features[feature.name] = QAPIDoc.ArgSection(self._parser,
+                                                             feature.name)
         self.features[feature.name].connect(feature)
 
     def check_expr(self, expr):
diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out
index 0ef85d959ac..bbf77b08dc3 100644
--- a/tests/qapi-schema/doc-good.out
+++ b/tests/qapi-schema/doc-good.out
@@ -158,7 +158,7 @@ doc symbol=Alternate
 
     arg=i
 an integer
-    @b is undocumented
+@b is undocumented
     arg=b
 
     feature=alt-feat
@@ -173,7 +173,7 @@ doc symbol=cmd
 the first argument
     arg=arg2
 the second
-       argument
+argument
     arg=arg3
 
     feature=cmd-feat1
-- 
2.20.1



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

* [PATCH v5 09/20] docs/sphinx: Add new qapi-doc Sphinx extension
  2020-08-10 19:49 [PATCH v5 00/20] Convert QAPI doc comments to generate rST instead of texinfo Peter Maydell
                   ` (7 preceding siblings ...)
  2020-08-10 19:50 ` [PATCH v5 08/20] scripts/qapi/parser.py: improve doc comment indent handling Peter Maydell
@ 2020-08-10 19:50 ` Peter Maydell
  2020-08-14 18:40   ` Richard Henderson
                     ` (2 more replies)
  2020-08-10 19:50 ` [PATCH v5 10/20] docs/interop: Convert qemu-ga-ref to rST Peter Maydell
                   ` (11 subsequent siblings)
  20 siblings, 3 replies; 62+ messages in thread
From: Peter Maydell @ 2020-08-10 19:50 UTC (permalink / raw)
  To: qemu-devel; +Cc: Markus Armbruster

Some of our documentation is auto-generated from documentation
comments in the JSON schema.

For Sphinx, rather than creating a file to include, the most natural
way to handle this is to have a small custom Sphinx extension which
processes the JSON file and inserts documentation into the rST
file being processed.

This is the same approach that kerneldoc and hxtool use.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
Changes v4->v5: match the changes in parameters to the
various visit_* methods from commit 7b3bc9e28f366
---
 docs/conf.py           |   6 +-
 docs/sphinx/qapidoc.py | 504 +++++++++++++++++++++++++++++++++++++++++
 MAINTAINERS            |   1 +
 3 files changed, 510 insertions(+), 1 deletion(-)
 create mode 100644 docs/sphinx/qapidoc.py

diff --git a/docs/conf.py b/docs/conf.py
index d6e173ef77b..44ec5050717 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -52,7 +52,10 @@ except NameError:
 # add these directories to sys.path here. If the directory is relative to the
 # documentation root, use an absolute path starting from qemu_docdir.
 #
+# Our extensions are in docs/sphinx; the qapidoc extension requires
+# the QAPI modules from scripts/.
 sys.path.insert(0, os.path.join(qemu_docdir, "sphinx"))
+sys.path.insert(0, os.path.join(qemu_docdir, "../scripts"))
 
 
 # -- General configuration ------------------------------------------------
@@ -67,7 +70,7 @@ needs_sphinx = '1.6'
 # Add any Sphinx extension module names here, as strings. They can be
 # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
 # ones.
-extensions = ['kerneldoc', 'qmp_lexer', 'hxtool']
+extensions = ['kerneldoc', 'qmp_lexer', 'hxtool', 'qapidoc']
 
 # Add any paths that contain templates here, relative to this directory.
 templates_path = ['_templates']
@@ -241,3 +244,4 @@ texinfo_documents = [
 kerneldoc_bin = os.path.join(qemu_docdir, '../scripts/kernel-doc')
 kerneldoc_srctree = os.path.join(qemu_docdir, '..')
 hxtool_srctree = os.path.join(qemu_docdir, '..')
+qapidoc_srctree = os.path.join(qemu_docdir, '..')
diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py
new file mode 100644
index 00000000000..4f571b2f45a
--- /dev/null
+++ b/docs/sphinx/qapidoc.py
@@ -0,0 +1,504 @@
+# coding=utf-8
+#
+# QEMU qapidoc QAPI file parsing extension
+#
+# Copyright (c) 2020 Linaro
+#
+# This work is licensed under the terms of the GNU GPLv2 or later.
+# See the COPYING file in the top-level directory.
+"""qapidoc is a Sphinx extension that implements the qapi-doc directive"""
+
+# The purpose of this extension is to read the documentation comments
+# in QAPI JSON schema files, and insert them all into the current document.
+# The conf.py file must set the qapidoc_srctree config value to
+# the root of the QEMU source tree.
+# Each qapi-doc:: directive takes one argument which is the
+# path of the .json file to process, relative to the source tree.
+
+import os
+import re
+
+from docutils import nodes
+from docutils.statemachine import ViewList
+from docutils.parsers.rst import directives, Directive
+from sphinx.errors import ExtensionError
+from sphinx.util.nodes import nested_parse_with_titles
+import sphinx
+from qapi.gen import QAPISchemaVisitor
+from qapi.schema import QAPIError, QAPISchema
+
+# Sphinx up to 1.6 uses AutodocReporter; 1.7 and later
+# use switch_source_input. Check borrowed from kerneldoc.py.
+Use_SSI = sphinx.__version__[:3] >= '1.7'
+if Use_SSI:
+    from sphinx.util.docutils import switch_source_input
+else:
+    from sphinx.ext.autodoc import AutodocReporter
+
+
+__version__ = '1.0'
+
+# Function borrowed from pydash, which is under the MIT license
+def intersperse(iterable, separator):
+    """Like join, but for arbitrary iterables, notably arrays"""
+    iterable = iter(iterable)
+    yield next(iterable)
+    for item in iterable:
+        yield separator
+        yield item
+
+class QAPISchemaGenRSTVisitor(QAPISchemaVisitor):
+    """A QAPI schema visitor which generates docutils/Sphinx nodes
+
+    This class builds up a tree of docutils/Sphinx nodes corresponding
+    to documentation for the various QAPI objects. To use it, first create
+    a QAPISchemaGenRSTVisitor object, and call its visit_begin() method.
+    Then you can call one of the two methods 'freeform' (to add documentation
+    for a freeform documentation chunk) or 'symbol' (to add documentation
+    for a QAPI symbol). These will cause the visitor to build up the
+    tree of document nodes. Once you've added all the documentation
+    via 'freeform' and 'symbol' method calls, you can call 'get_document_nodes'
+    to get the final list of document nodes (in a form suitable for returning
+    from a Sphinx directive's 'run' method).
+    """
+    def __init__(self, sphinx_directive):
+        self._cur_doc = None
+        self._sphinx_directive = sphinx_directive
+        self._top_node = nodes.section()
+        self._active_headings = [self._top_node]
+
+    def _serror(self, msg):
+        """Raise an exception giving a user-friendly syntax error message"""
+        file = self._cur_doc.info.fname
+        line = self._cur_doc.info.line
+        raise ExtensionError('%s line %d: syntax error: %s' % (file, line, msg))
+
+    def _make_dlitem(self, term, defn):
+        """Return a dlitem node with the specified term and definition.
+
+        term should be a list of Text and literal nodes.
+        defn should be one of:
+        - a string, which will be handed to _parse_text_into_node
+        - a list of Text and literal nodes, which will be put into
+          a paragraph node
+        """
+        dlitem = nodes.definition_list_item()
+        dlterm = nodes.term('', '', *term)
+        dlitem += dlterm
+        if defn:
+            dldef = nodes.definition()
+            if isinstance(defn, list):
+                dldef += nodes.paragraph('', '', *defn)
+            else:
+                self._parse_text_into_node(defn, dldef)
+            dlitem += dldef
+        return dlitem
+
+    def _make_section(self, title):
+        """Return a section node with optional title"""
+        section = nodes.section(ids=[self._sphinx_directive.new_serialno()])
+        if title:
+            section += nodes.title(title, title)
+        return section
+
+    def _nodes_for_ifcond(self, ifcond, with_if=True):
+        """Return list of Text, literal nodes for the ifcond
+
+        Return a list which gives text like ' (If: cond1, cond2, cond3)', where
+        the conditions are in literal-text and the commas are not.
+        If with_if is False, we don't return the "(If: " and ")".
+        """
+        condlist = intersperse([nodes.literal('', c) for c in ifcond],
+                               nodes.Text(', '))
+        if not with_if:
+            return condlist
+
+        nodelist = [nodes.Text(' ('), nodes.strong('', 'If: ')]
+        nodelist.extend(condlist)
+        nodelist.append(nodes.Text(')'))
+        return nodelist
+
+    def _nodes_for_one_member(self, member):
+        """Return list of Text, literal nodes for this member
+
+        Return a list of doctree nodes which give text like
+        'name: type (optional) (If: ...)' suitable for use as the
+        'term' part of a definition list item.
+        """
+        term = [nodes.literal('', member.name)]
+        if member.type.doc_type():
+            term.append(nodes.Text(': '))
+            term.append(nodes.literal('', member.type.doc_type()))
+        if member.optional:
+            term.append(nodes.Text(' (optional)'))
+        if member.ifcond:
+            term.extend(self._nodes_for_ifcond(member.ifcond))
+        return term
+
+    def _nodes_for_variant_when(self, variants, variant):
+        """Return list of Text, literal nodes for variant 'when' clause
+
+        Return a list of doctree nodes which give text like
+        'when tagname is variant (If: ...)' suitable for use in
+        the 'variants' part of a definition list.
+        """
+        term = [nodes.Text(' when '),
+                nodes.literal('', variants.tag_member.name),
+                nodes.Text(' is '),
+                nodes.literal('', '"%s"' % variant.name)]
+        if variant.ifcond:
+            term.extend(self._nodes_for_ifcond(variant.ifcond))
+        return term
+
+    def _nodes_for_members(self, doc, what, base=None, variants=None):
+        """Return doctree nodes for the table of members"""
+        dlnode = nodes.definition_list()
+        for section in doc.args.values():
+            term = self._nodes_for_one_member(section.member)
+            # TODO drop fallbacks when undocumented members are outlawed
+            if section.text:
+                defn = section.text
+            elif (variants and variants.tag_member == section.member
+                  and not section.member.type.doc_type()):
+                values = section.member.type.member_names()
+                defn = [nodes.Text('One of ')]
+                defn.extend(intersperse([nodes.literal('', v) for v in values],
+                                        nodes.Text(', ')))
+            else:
+                defn = [nodes.Text('Not documented')]
+
+            dlnode += self._make_dlitem(term, defn)
+
+        if base:
+            dlnode += self._make_dlitem([nodes.Text('The members of '),
+                                         nodes.literal('', base.doc_type())],
+                                        None)
+
+        if variants:
+            for v in variants.variants:
+                if v.type.is_implicit():
+                    assert not v.type.base and not v.type.variants
+                    for m in v.type.local_members:
+                        term = self._nodes_for_one_member(m)
+                        term.extend(self._nodes_for_variant_when(variants, v))
+                        dlnode += self._make_dlitem(term, None)
+                else:
+                    term = [nodes.Text('The members of '),
+                            nodes.literal('', v.type.doc_type())]
+                    term.extend(self._nodes_for_variant_when(variants, v))
+                    dlnode += self._make_dlitem(term, None)
+
+        if not dlnode.children:
+            return None
+
+        section = self._make_section(what)
+        section += dlnode
+        return section
+
+    def _nodes_for_enum_values(self, doc, what):
+        """Return doctree nodes for the table of enum values"""
+        seen_item = False
+        dlnode = nodes.definition_list()
+        for section in doc.args.values():
+            termtext = [nodes.literal('', section.member.name)]
+            if section.member.ifcond:
+                termtext.extend(self._nodes_for_ifcond(section.member.ifcond))
+            # TODO drop fallbacks when undocumented members are outlawed
+            if section.text:
+                defn = section.text
+            else:
+                defn = [nodes.Text('Not documented')]
+
+            dlnode += self._make_dlitem(termtext, defn)
+            seen_item = True
+
+        if not seen_item:
+            return None
+
+        section = self._make_section(what)
+        section += dlnode
+        return section
+
+    def _nodes_for_arguments(self, doc, boxed_arg_type):
+        """Return doctree nodes for the arguments section"""
+        if boxed_arg_type:
+            assert not doc.args
+            section = self._make_section('Arguments')
+            dlnode = nodes.definition_list()
+            dlnode += self._make_dlitem(
+                [nodes.Text('The members of '),
+                 nodes.literal('', boxed_arg_type.name)],
+                None)
+            section += dlnode
+            return section
+
+        return self._nodes_for_members(doc, 'Arguments')
+
+    def _nodes_for_features(self, doc):
+        """Return doctree nodes for the table of features"""
+        seen_item = False
+        dlnode = nodes.definition_list()
+        for section in doc.features.values():
+            dlnode += self._make_dlitem([nodes.literal('', section.name)],
+                                        section.text)
+            seen_item = True
+
+        if not seen_item:
+            return None
+
+        section = self._make_section('Features')
+        section += dlnode
+        return section
+
+    def _nodes_for_example(self, exampletext):
+        """Return doctree nodes for a code example snippet"""
+        return nodes.literal_block(exampletext, exampletext)
+
+    def _nodes_for_sections(self, doc, ifcond):
+        """Return doctree nodes for additional sections following arguments"""
+        nodelist = []
+        for section in doc.sections:
+            snode = self._make_section(section.name)
+            if section.name and section.name.startswith('Example'):
+                snode += self._nodes_for_example(section.text)
+            else:
+                self._parse_text_into_node(section.text, snode)
+            nodelist.append(snode)
+        if ifcond:
+            snode = self._make_section('If')
+            snode += self._nodes_for_ifcond(ifcond, with_if=False)
+            nodelist.append(snode)
+        if not nodelist:
+            return None
+        return nodelist
+
+    def _add_doc(self, typ, sections):
+        """Add documentation for a command/object/enum...
+
+        We assume we're documenting the thing defined in self._cur_doc.
+        typ is the type of thing being added ("Command", "Object", etc)
+
+        sections is a list of nodes for sections to add to the definition.
+        """
+
+        doc = self._cur_doc
+        snode = nodes.section(ids=[self._sphinx_directive.new_serialno()])
+        snode += nodes.title('', '', *[nodes.literal(doc.symbol, doc.symbol),
+                                       nodes.Text(' (' + typ + ')')])
+        self._parse_text_into_node(doc.body.text, snode)
+        for s in sections:
+            if s is not None:
+                snode += s
+        self._add_node_to_current_heading(snode)
+
+    def visit_enum_type(self, name, info, ifcond, features, members, prefix):
+        doc = self._cur_doc
+        self._add_doc('Enum',
+                      [self._nodes_for_enum_values(doc, 'Values'),
+                       self._nodes_for_features(doc),
+                       self._nodes_for_sections(doc, ifcond)])
+
+    def visit_object_type(self, name, info, ifcond, features,
+                          base, members, variants):
+        doc = self._cur_doc
+        if base and base.is_implicit():
+            base = None
+        self._add_doc('Object',
+                      [self._nodes_for_members(doc, 'Members', base, variants),
+                       self._nodes_for_features(doc),
+                       self._nodes_for_sections(doc, ifcond)])
+
+    def visit_alternate_type(self, name, info, ifcond, features, variants):
+        doc = self._cur_doc
+        self._add_doc('Alternate',
+                      [self._nodes_for_members(doc, 'Members'),
+                       self._nodes_for_features(doc),
+                       self._nodes_for_sections(doc, ifcond)])
+
+    def visit_command(self, name, info, ifcond, features, arg_type,
+                      ret_type, gen, success_response, boxed, allow_oob,
+                      allow_preconfig):
+        doc = self._cur_doc
+        self._add_doc('Command',
+                      [self._nodes_for_arguments(doc,
+                                                 arg_type if boxed else None),
+                       self._nodes_for_features(doc),
+                       self._nodes_for_sections(doc, ifcond)])
+
+    def visit_event(self, name, info, ifcond, features, arg_type, boxed):
+        doc = self._cur_doc
+        self._add_doc('Event',
+                      [self._nodes_for_arguments(doc,
+                                                 arg_type if boxed else None),
+                       self._nodes_for_features(doc),
+                       self._nodes_for_sections(doc, ifcond)])
+
+    def symbol(self, doc, entity):
+        """Add documentation for one symbol to the document tree
+
+        This is the main entry point which causes us to add documentation
+        nodes for a symbol (which could be a 'command', 'object', 'event',
+        etc). We do this by calling 'visit' on the schema entity, which
+        will then call back into one of our visit_* methods, depending
+        on what kind of thing this symbol is.
+        """
+        self._cur_doc = doc
+        entity.visit(self)
+        self._cur_doc = None
+
+    def _start_new_heading(self, heading, level):
+        """Start a new heading at the specified heading level
+
+        Create a new section whose title is 'heading' and which is placed
+        in the docutils node tree as a child of the most recent level-1
+        heading. Subsequent document sections (commands, freeform doc chunks,
+        etc) will be placed as children of this new heading section.
+        """
+        if len(self._active_headings) < level:
+            self._serror('Level %d subheading found outside a level %d heading'
+                         % (level, level - 1))
+        snode = self._make_section(heading)
+        self._active_headings[level - 1] += snode
+        self._active_headings = self._active_headings[:level]
+        self._active_headings.append(snode)
+
+    def _add_node_to_current_heading(self, node):
+        """Add the node to whatever the current active heading is"""
+        self._active_headings[-1] += node
+
+    def freeform(self, doc):
+        """Add a piece of 'freeform' documentation to the document tree
+
+        A 'freeform' document chunk doesn't relate to any particular
+        symbol (for instance, it could be an introduction).
+
+        As a special case, if the freeform document is a single line
+        of the form '= Heading text' it is treated as a section or subsection
+        heading, with the heading level indicated by the number of '=' signs.
+        """
+
+        # QAPIDoc documentation says free-form documentation blocks
+        # must have only a body section, nothing else.
+        assert not doc.sections
+        assert not doc.args
+        assert not doc.features
+        self._cur_doc = doc
+
+        if re.match(r'=+ ', doc.body.text):
+            # Section or subsection heading: must be the only thing in the block
+            (heading, _, rest) = doc.body.text.partition('\n')
+            if rest != '':
+                raise ExtensionError('%s line %s: section or subsection heading'
+                                     ' must be in its own doc comment block'
+                                     % (doc.info.fname, doc.info.line))
+            (leader, _, heading) = heading.partition(' ')
+            self._start_new_heading(heading, len(leader))
+            return
+
+        node = self._make_section(None)
+        self._parse_text_into_node(doc.body.text, node)
+        self._add_node_to_current_heading(node)
+        self._cur_doc = None
+
+    def _parse_text_into_node(self, doctext, node):
+        """Parse a chunk of QAPI-doc-format text into the node
+
+        The doc comment can contain most inline rST markup, including
+        bulleted and enumerated lists.
+        As an extra permitted piece of markup, @var will be turned
+        into ``var``.
+        """
+
+        # Handle the "@var means ``var`` case
+        doctext = re.sub(r'@([\w-]+)', r'``\1``', doctext)
+
+        rstlist = ViewList()
+        for line in doctext.splitlines():
+            # The reported line number will always be that of the start line
+            # of the doc comment, rather than the actual location of the error.
+            # Being more precise would require overhaul of the QAPIDoc class
+            # to track lines more exactly within all the sub-parts of the doc
+            # comment, as well as counting lines here.
+            rstlist.append(line, self._cur_doc.info.fname,
+                           self._cur_doc.info.line)
+        self._sphinx_directive.do_parse(rstlist, node)
+
+    def get_document_nodes(self):
+        """Return the list of docutils nodes which make up the document"""
+        return self._top_node.children
+
+class QAPIDocDirective(Directive):
+    """Extract documentation from the specified QAPI .json file"""
+    required_argument = 1
+    optional_arguments = 1
+    option_spec = {
+        'qapifile': directives.unchanged_required
+    }
+    has_content = False
+
+    def new_serialno(self):
+        """Return a unique new ID string suitable for use as a node's ID"""
+        env = self.state.document.settings.env
+        return 'qapidoc-%d' % env.new_serialno('qapidoc')
+
+    def run(self):
+        env = self.state.document.settings.env
+        qapifile = env.config.qapidoc_srctree + '/' + self.arguments[0]
+
+        # Tell sphinx of the dependency
+        env.note_dependency(os.path.abspath(qapifile))
+
+        try:
+            schema = QAPISchema(qapifile)
+        except QAPIError as err:
+            # Launder QAPI parse errors into Sphinx extension errors
+            # so they are displayed nicely to the user
+            raise ExtensionError(str(err))
+
+        vis = QAPISchemaGenRSTVisitor(self)
+        vis.visit_begin(schema)
+        for doc in schema.docs:
+            if doc.symbol:
+                vis.symbol(doc, schema.lookup_entity(doc.symbol))
+            else:
+                vis.freeform(doc)
+
+        return vis.get_document_nodes()
+
+    def do_parse(self, rstlist, node):
+        """Parse rST source lines and add them to the specified node
+
+        Take the list of rST source lines rstlist, parse them as
+        rST, and add the resulting docutils nodes as children of node.
+        The nodes are parsed in a way that allows them to include
+        subheadings (titles) without confusing the rendering of
+        anything else.
+        """
+        # This is from kerneldoc.py -- it works around an API change in
+        # Sphinx between 1.6 and 1.7. Unlike kerneldoc.py, we use
+        # sphinx.util.nodes.nested_parse_with_titles() rather than the
+        # plain self.state.nested_parse(), and so we can drop the saving
+        # of title_styles and section_level that kerneldoc.py does,
+        # because nested_parse_with_titles() does that for us.
+        if Use_SSI:
+            with switch_source_input(self.state, rstlist):
+                nested_parse_with_titles(self.state, rstlist, node)
+        else:
+            save = self.state.memo.reporter
+            self.state.memo.reporter = AutodocReporter(rstlist,
+                                                       self.state.memo.reporter)
+            try:
+                nested_parse_with_titles(self.state, rstlist, node)
+            finally:
+                self.state.memo.reporter = save
+
+def setup(app):
+    """ Register qapi-doc directive with Sphinx"""
+    app.add_config_value('qapidoc_srctree', None, 'env')
+    app.add_directive('qapi-doc', QAPIDocDirective)
+
+    return dict(
+        version=__version__,
+        parallel_read_safe=True,
+        parallel_write_safe=True
+    )
diff --git a/MAINTAINERS b/MAINTAINERS
index 0886eb3d2b5..fdd38f412d7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3116,6 +3116,7 @@ M: Peter Maydell <peter.maydell@linaro.org>
 S: Maintained
 F: docs/conf.py
 F: docs/*/conf.py
+F: docs/sphinx/
 
 Miscellaneous
 -------------
-- 
2.20.1



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

* [PATCH v5 10/20] docs/interop: Convert qemu-ga-ref to rST
  2020-08-10 19:49 [PATCH v5 00/20] Convert QAPI doc comments to generate rST instead of texinfo Peter Maydell
                   ` (8 preceding siblings ...)
  2020-08-10 19:50 ` [PATCH v5 09/20] docs/sphinx: Add new qapi-doc Sphinx extension Peter Maydell
@ 2020-08-10 19:50 ` Peter Maydell
  2020-09-04 13:16   ` Markus Armbruster
  2020-08-10 19:50 ` [PATCH v5 11/20] docs/interop: Convert qemu-qmp-ref " Peter Maydell
                   ` (10 subsequent siblings)
  20 siblings, 1 reply; 62+ messages in thread
From: Peter Maydell @ 2020-08-10 19:50 UTC (permalink / raw)
  To: qemu-devel; +Cc: Markus Armbruster

Convert qemu-ga-ref to rST format. This includes dropping
the plain-text, pdf and info format outputs for this document;
as with all our other Sphinx-based documentation, we provide
HTML and manpage only.

The qemu-ga-ref.rst is somewhat more stripped down than
the .texi was, because we do not (currently) attempt to
generate indexes for the commands, events and data types
being documented.

As the GA ref is now part of the Sphinx 'interop' manual,
we can delete the direct link from index.html.in.

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 docs/index.html.in            |  1 -
 docs/interop/conf.py          |  2 +
 docs/interop/index.rst        |  1 +
 docs/interop/qemu-ga-ref.rst  |  4 ++
 docs/interop/qemu-ga-ref.texi | 80 -----------------------------------
 Makefile                      | 42 ++++++++----------
 MAINTAINERS                   |  2 +-
 7 files changed, 25 insertions(+), 107 deletions(-)
 create mode 100644 docs/interop/qemu-ga-ref.rst
 delete mode 100644 docs/interop/qemu-ga-ref.texi

diff --git a/docs/index.html.in b/docs/index.html.in
index 6736fa4360c..99d4d8075fc 100644
--- a/docs/index.html.in
+++ b/docs/index.html.in
@@ -13,7 +13,6 @@
             <li><a href="interop/index.html">System Emulation Management and Interoperability Guide</a></li>
             <li><a href="specs/index.html">System Emulation Guest Hardware Specifications</a></li>
             <li><a href="interop/qemu-qmp-ref.html">QMP Reference Manual</a></li>
-            <li><a href="interop/qemu-ga-ref.html">Guest Agent Protocol Reference</a></li>
         </ul>
     </body>
 </html>
diff --git a/docs/interop/conf.py b/docs/interop/conf.py
index 42ce7e3d365..e83632e0108 100644
--- a/docs/interop/conf.py
+++ b/docs/interop/conf.py
@@ -19,4 +19,6 @@ html_theme_options['description'] = u'System Emulation Management and Interopera
 man_pages = [
     ('qemu-ga', 'qemu-ga', u'QEMU Guest Agent',
      ['Michael Roth <mdroth@linux.vnet.ibm.com>'], 8),
+    ('qemu-ga-ref', 'qemu-ga-ref', u'QEMU Guest Agent Protocol Reference',
+     [], 7),
 ]
diff --git a/docs/interop/index.rst b/docs/interop/index.rst
index 006f9864208..738cdbe185e 100644
--- a/docs/interop/index.rst
+++ b/docs/interop/index.rst
@@ -18,6 +18,7 @@ Contents:
    live-block-operations
    pr-helper
    qemu-ga
+   qemu-ga-ref
    vhost-user
    vhost-user-gpu
    vhost-vdpa
diff --git a/docs/interop/qemu-ga-ref.rst b/docs/interop/qemu-ga-ref.rst
new file mode 100644
index 00000000000..013eac0bb53
--- /dev/null
+++ b/docs/interop/qemu-ga-ref.rst
@@ -0,0 +1,4 @@
+QEMU Guest Agent Protocol Reference
+===================================
+
+.. qapi-doc:: qga/qapi-schema.json
diff --git a/docs/interop/qemu-ga-ref.texi b/docs/interop/qemu-ga-ref.texi
deleted file mode 100644
index ddb76ce1c2a..00000000000
--- a/docs/interop/qemu-ga-ref.texi
+++ /dev/null
@@ -1,80 +0,0 @@
-\input texinfo
-@setfilename qemu-ga-ref.info
-
-@include version.texi
-
-@exampleindent 0
-@paragraphindent 0
-
-@settitle QEMU Guest Agent Protocol Reference
-
-@iftex
-@center @image{docs/qemu_logo}
-@end iftex
-
-@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
-
-@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/Makefile b/Makefile
index 13dd708c4af..f0cca10b427 100644
--- a/Makefile
+++ b/Makefile
@@ -371,7 +371,7 @@ DOCS+=$(MANUAL_BUILDDIR)/tools/virtiofsd.1
 endif
 DOCS+=$(MANUAL_BUILDDIR)/system/qemu-block-drivers.7
 DOCS+=docs/interop/qemu-qmp-ref.html docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7
-DOCS+=docs/interop/qemu-ga-ref.html docs/interop/qemu-ga-ref.txt docs/interop/qemu-ga-ref.7
+DOCS+=$(MANUAL_BUILDDIR)/interop/qemu-ga-ref.7
 DOCS+=$(MANUAL_BUILDDIR)/system/qemu-cpu-models.7
 DOCS+=$(MANUAL_BUILDDIR)/index.html
 ifdef CONFIG_VIRTFS
@@ -800,11 +800,11 @@ distclean: clean
 	rm -f config.log
 	rm -f linux-headers/asm
 	rm -f docs/version.texi
-	rm -f docs/interop/qemu-ga-qapi.texi docs/interop/qemu-qmp-qapi.texi
-	rm -f docs/interop/qemu-qmp-ref.7 docs/interop/qemu-ga-ref.7
-	rm -f docs/interop/qemu-qmp-ref.txt docs/interop/qemu-ga-ref.txt
-	rm -f docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf
-	rm -f docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html
+	rm -f docs/interop/qemu-qmp-qapi.texi
+	rm -f docs/interop/qemu-qmp-ref.7
+	rm -f docs/interop/qemu-qmp-ref.txt
+	rm -f docs/interop/qemu-qmp-ref.pdf
+	rm -f docs/interop/qemu-qmp-ref.html
 	rm -rf .doctrees
 	$(call clean-manual,devel)
 	$(call clean-manual,interop)
@@ -858,7 +858,7 @@ endif
 # and also any sphinx-built manpages.
 define install-manual =
 for d in $$(cd $(MANUAL_BUILDDIR) && find $1 -type d); do $(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)/$$d"; done
-for f in $$(cd $(MANUAL_BUILDDIR) && find $1 -type f -a '!' '(' -name '*.[0-9]' -o -name 'qemu-*-qapi.*' -o -name 'qemu-*-ref.*' ')' ); do $(INSTALL_DATA) "$(MANUAL_BUILDDIR)/$$f" "$(DESTDIR)$(qemu_docdir)/$$f"; done
+for f in $$(cd $(MANUAL_BUILDDIR) && find $1 -type f -a '!' '(' -name '*.[0-9]' -o -name 'qemu-*-qapi.*' -o -name 'qemu-qmp-ref.*' ')' ); do $(INSTALL_DATA) "$(MANUAL_BUILDDIR)/$$f" "$(DESTDIR)$(qemu_docdir)/$$f"; done
 endef
 
 # Note that we deliberately do not install the "devel" manual: it is
@@ -894,10 +894,7 @@ ifdef CONFIG_TRACE_SYSTEMTAP
 endif
 ifneq (,$(findstring qemu-ga,$(TOOLS)))
 	$(INSTALL_DATA) $(MANUAL_BUILDDIR)/interop/qemu-ga.8 "$(DESTDIR)$(mandir)/man8"
-	$(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)/interop"
-	$(INSTALL_DATA) docs/interop/qemu-ga-ref.html "$(DESTDIR)$(qemu_docdir)/interop"
-	$(INSTALL_DATA) docs/interop/qemu-ga-ref.txt "$(DESTDIR)$(qemu_docdir)/interop"
-	$(INSTALL_DATA) docs/interop/qemu-ga-ref.7 "$(DESTDIR)$(mandir)/man7"
+	$(INSTALL_DATA) $(MANUAL_BUILDDIR)/interop/qemu-ga-ref.7 "$(DESTDIR)$(mandir)/man7"
 endif
 endif
 ifdef CONFIG_VIRTFS
@@ -1090,7 +1087,7 @@ endef
 $(MANUAL_BUILDDIR)/devel/index.html: $(call manual-deps,devel)
 	$(call build-manual,devel,html)
 
-$(MANUAL_BUILDDIR)/interop/index.html: $(call manual-deps,interop)
+$(MANUAL_BUILDDIR)/interop/index.html: $(call manual-deps,interop) $(SRC_PATH)/qga/qapi-schema.json $(qapi-py)
 	$(call build-manual,interop,html)
 
 $(MANUAL_BUILDDIR)/specs/index.html: $(call manual-deps,specs)
@@ -1105,7 +1102,10 @@ $(MANUAL_BUILDDIR)/tools/index.html: $(call manual-deps,tools) $(SRC_PATH)/qemu-
 $(MANUAL_BUILDDIR)/user/index.html: $(call manual-deps,user)
 	$(call build-manual,user,html)
 
-$(call define-manpage-rule,interop,qemu-ga.8)
+$(call define-manpage-rule,interop,\
+       qemu-ga.8 qemu-ga-ref.7,\
+       $(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/qga/qapi-schema.json \
+       $(qapi-py))
 
 $(call define-manpage-rule,system,qemu.1 qemu-block-drivers.7 qemu-cpu-models.7)
 
@@ -1122,18 +1122,10 @@ $(MANUAL_BUILDDIR)/index.html: $(SRC_PATH)/docs/index.html.in qemu-version.h
 docs/interop/qemu-qmp-qapi.texi: qapi/qapi-doc.texi
 	@cp -p $< $@
 
-docs/interop/qemu-ga-qapi.texi: qga/qapi-generated/qga-qapi-doc.texi
-	@cp -p $< $@
-
-html: docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html sphinxdocs
-info: docs/interop/qemu-qmp-ref.info docs/interop/qemu-ga-ref.info
-pdf: docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf
-txt: docs/interop/qemu-qmp-ref.txt docs/interop/qemu-ga-ref.txt
-
-docs/interop/qemu-ga-ref.dvi docs/interop/qemu-ga-ref.html \
-    docs/interop/qemu-ga-ref.info docs/interop/qemu-ga-ref.pdf \
-    docs/interop/qemu-ga-ref.txt docs/interop/qemu-ga-ref.7: \
-	docs/interop/qemu-ga-ref.texi docs/interop/qemu-ga-qapi.texi
+html: docs/interop/qemu-qmp-ref.html sphinxdocs
+info: docs/interop/qemu-qmp-ref.info
+pdf: docs/interop/qemu-qmp-ref.pdf
+txt: docs/interop/qemu-qmp-ref.txt
 
 docs/interop/qemu-qmp-ref.dvi docs/interop/qemu-qmp-ref.html \
     docs/interop/qemu-qmp-ref.info docs/interop/qemu-qmp-ref.pdf \
diff --git a/MAINTAINERS b/MAINTAINERS
index fdd38f412d7..6eae3625733 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2394,9 +2394,9 @@ M: Michael Roth <mdroth@linux.vnet.ibm.com>
 S: Maintained
 F: qga/
 F: docs/interop/qemu-ga.rst
+F: docs/interop/qemu-ga-ref.rst
 F: scripts/qemu-guest-agent/
 F: tests/test-qga.c
-F: docs/interop/qemu-ga-ref.texi
 T: git https://github.com/mdroth/qemu.git qga
 
 QOM
-- 
2.20.1



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

* [PATCH v5 11/20] docs/interop: Convert qemu-qmp-ref to rST
  2020-08-10 19:49 [PATCH v5 00/20] Convert QAPI doc comments to generate rST instead of texinfo Peter Maydell
                   ` (9 preceding siblings ...)
  2020-08-10 19:50 ` [PATCH v5 10/20] docs/interop: Convert qemu-ga-ref to rST Peter Maydell
@ 2020-08-10 19:50 ` Peter Maydell
  2020-08-10 19:50 ` [PATCH v5 12/20] qapi: Use rST markup for literal blocks Peter Maydell
                   ` (9 subsequent siblings)
  20 siblings, 0 replies; 62+ messages in thread
From: Peter Maydell @ 2020-08-10 19:50 UTC (permalink / raw)
  To: qemu-devel; +Cc: Markus Armbruster

Convert qemu-qmp-ref to rST format. This includes dropping
the plain-text, pdf and info format outputs for this document;
as with all our other Sphinx-based documentation, we provide
HTML and manpage only.

The qemu-qmp-ref.rst is somewhat more stripped down than
the .texi was, because we do not (currently) attempt to
generate indexes for the commands, events and data types
being documented.

Again, we drop the direct link from index.html.in now that
the QMP ref is part of the interop manual.

This commit removes the 'info', 'txt' and 'pdf' Makefile
targets, because we no longer generate any documentation
except for the Sphinx HTML manuals and the manpages.

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 docs/index.html.in             |  1 -
 docs/interop/conf.py           |  2 +
 docs/interop/index.rst         |  1 +
 docs/interop/qemu-qmp-ref.rst  |  4 ++
 docs/interop/qemu-qmp-ref.texi | 80 ----------------------------------
 Makefile                       | 38 ++++------------
 6 files changed, 15 insertions(+), 111 deletions(-)
 create mode 100644 docs/interop/qemu-qmp-ref.rst
 delete mode 100644 docs/interop/qemu-qmp-ref.texi

diff --git a/docs/index.html.in b/docs/index.html.in
index 99d4d8075fc..b3dfb7a4611 100644
--- a/docs/index.html.in
+++ b/docs/index.html.in
@@ -12,7 +12,6 @@
             <li><a href="tools/index.html">Tools Guide</a></li>
             <li><a href="interop/index.html">System Emulation Management and Interoperability Guide</a></li>
             <li><a href="specs/index.html">System Emulation Guest Hardware Specifications</a></li>
-            <li><a href="interop/qemu-qmp-ref.html">QMP Reference Manual</a></li>
         </ul>
     </body>
 </html>
diff --git a/docs/interop/conf.py b/docs/interop/conf.py
index e83632e0108..43de386d33d 100644
--- a/docs/interop/conf.py
+++ b/docs/interop/conf.py
@@ -21,4 +21,6 @@ man_pages = [
      ['Michael Roth <mdroth@linux.vnet.ibm.com>'], 8),
     ('qemu-ga-ref', 'qemu-ga-ref', u'QEMU Guest Agent Protocol Reference',
      [], 7),
+    ('qemu-qmp-ref', 'qemu-qmp-ref', u'QEMU QMP Reference Manual',
+     [], 7),
 ]
diff --git a/docs/interop/index.rst b/docs/interop/index.rst
index 738cdbe185e..cd78d679d82 100644
--- a/docs/interop/index.rst
+++ b/docs/interop/index.rst
@@ -19,6 +19,7 @@ Contents:
    pr-helper
    qemu-ga
    qemu-ga-ref
+   qemu-qmp-ref
    vhost-user
    vhost-user-gpu
    vhost-vdpa
diff --git a/docs/interop/qemu-qmp-ref.rst b/docs/interop/qemu-qmp-ref.rst
new file mode 100644
index 00000000000..e640903abaf
--- /dev/null
+++ b/docs/interop/qemu-qmp-ref.rst
@@ -0,0 +1,4 @@
+QEMU QMP Reference Manual
+=========================
+
+.. qapi-doc:: qapi/qapi-schema.json
diff --git a/docs/interop/qemu-qmp-ref.texi b/docs/interop/qemu-qmp-ref.texi
deleted file mode 100644
index bb25758bd02..00000000000
--- a/docs/interop/qemu-qmp-ref.texi
+++ /dev/null
@@ -1,80 +0,0 @@
-\input texinfo
-@setfilename qemu-qmp-ref.info
-
-@include version.texi
-
-@exampleindent 0
-@paragraphindent 0
-
-@settitle QEMU QMP Reference Manual
-
-@iftex
-@center @image{docs/qemu_logo}
-@end iftex
-
-@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-qmp-qapi.texi
-
-@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/Makefile b/Makefile
index f0cca10b427..3df1cf68333 100644
--- a/Makefile
+++ b/Makefile
@@ -123,7 +123,6 @@ GENERATED_QAPI_FILES += qapi/qapi-events.h qapi/qapi-events.c
 GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-events-%.h)
 GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-events-%.c)
 GENERATED_QAPI_FILES += qapi/qapi-introspect.c qapi/qapi-introspect.h
-GENERATED_QAPI_FILES += qapi/qapi-doc.texi
 
 # The following list considers only the storage daemon main module. All other
 # modules are currently shared with the main schema, so we don't actually
@@ -370,7 +369,6 @@ ifeq ($(CONFIG_LINUX)$(CONFIG_SECCOMP)$(CONFIG_LIBCAP_NG),yyy)
 DOCS+=$(MANUAL_BUILDDIR)/tools/virtiofsd.1
 endif
 DOCS+=$(MANUAL_BUILDDIR)/system/qemu-block-drivers.7
-DOCS+=docs/interop/qemu-qmp-ref.html docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7
 DOCS+=$(MANUAL_BUILDDIR)/interop/qemu-ga-ref.7
 DOCS+=$(MANUAL_BUILDDIR)/system/qemu-cpu-models.7
 DOCS+=$(MANUAL_BUILDDIR)/index.html
@@ -644,8 +642,7 @@ $(SRC_PATH)/scripts/qapi-gen.py
 qga/qapi-generated/qga-qapi-types.c qga/qapi-generated/qga-qapi-types.h \
 qga/qapi-generated/qga-qapi-visit.c qga/qapi-generated/qga-qapi-visit.h \
 qga/qapi-generated/qga-qapi-commands.h qga/qapi-generated/qga-qapi-commands.c \
-qga/qapi-generated/qga-qapi-init-commands.h qga/qapi-generated/qga-qapi-init-commands.c \
-qga/qapi-generated/qga-qapi-doc.texi: \
+qga/qapi-generated/qga-qapi-init-commands.h qga/qapi-generated/qga-qapi-init-commands.c: \
 qga/qapi-generated/qapi-gen-timestamp ;
 qga/qapi-generated/qapi-gen-timestamp: $(SRC_PATH)/qga/qapi-schema.json $(qapi-py)
 	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \
@@ -800,11 +797,6 @@ distclean: clean
 	rm -f config.log
 	rm -f linux-headers/asm
 	rm -f docs/version.texi
-	rm -f docs/interop/qemu-qmp-qapi.texi
-	rm -f docs/interop/qemu-qmp-ref.7
-	rm -f docs/interop/qemu-qmp-ref.txt
-	rm -f docs/interop/qemu-qmp-ref.pdf
-	rm -f docs/interop/qemu-qmp-ref.html
 	rm -rf .doctrees
 	$(call clean-manual,devel)
 	$(call clean-manual,interop)
@@ -858,7 +850,7 @@ endif
 # and also any sphinx-built manpages.
 define install-manual =
 for d in $$(cd $(MANUAL_BUILDDIR) && find $1 -type d); do $(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)/$$d"; done
-for f in $$(cd $(MANUAL_BUILDDIR) && find $1 -type f -a '!' '(' -name '*.[0-9]' -o -name 'qemu-*-qapi.*' -o -name 'qemu-qmp-ref.*' ')' ); do $(INSTALL_DATA) "$(MANUAL_BUILDDIR)/$$f" "$(DESTDIR)$(qemu_docdir)/$$f"; done
+for f in $$(cd $(MANUAL_BUILDDIR) && find $1 -type f -a '!' -name '*.[0-9]'); do $(INSTALL_DATA) "$(MANUAL_BUILDDIR)/$$f" "$(DESTDIR)$(qemu_docdir)/$$f"; done
 endef
 
 # Note that we deliberately do not install the "devel" manual: it is
@@ -874,14 +866,11 @@ install-sphinxdocs: sphinxdocs
 install-doc: $(DOCS) install-sphinxdocs
 	$(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)"
 	$(INSTALL_DATA) $(MANUAL_BUILDDIR)/index.html "$(DESTDIR)$(qemu_docdir)"
-	$(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)/interop"
-	$(INSTALL_DATA) docs/interop/qemu-qmp-ref.html "$(DESTDIR)$(qemu_docdir)/interop"
-	$(INSTALL_DATA) docs/interop/qemu-qmp-ref.txt "$(DESTDIR)$(qemu_docdir)/interop"
 ifdef CONFIG_POSIX
 	$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1"
 	$(INSTALL_DATA) $(MANUAL_BUILDDIR)/system/qemu.1 "$(DESTDIR)$(mandir)/man1"
 	$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man7"
-	$(INSTALL_DATA) docs/interop/qemu-qmp-ref.7 "$(DESTDIR)$(mandir)/man7"
+	$(INSTALL_DATA) $(MANUAL_BUILDDIR)/interop/qemu-qmp-ref.7 "$(DESTDIR)$(mandir)/man7"
 	$(INSTALL_DATA) $(MANUAL_BUILDDIR)/system/qemu-block-drivers.7 "$(DESTDIR)$(mandir)/man7"
 	$(INSTALL_DATA) $(MANUAL_BUILDDIR)/system/qemu-cpu-models.7 "$(DESTDIR)$(mandir)/man7"
 ifeq ($(CONFIG_TOOLS),y)
@@ -1087,7 +1076,7 @@ endef
 $(MANUAL_BUILDDIR)/devel/index.html: $(call manual-deps,devel)
 	$(call build-manual,devel,html)
 
-$(MANUAL_BUILDDIR)/interop/index.html: $(call manual-deps,interop) $(SRC_PATH)/qga/qapi-schema.json $(qapi-py)
+$(MANUAL_BUILDDIR)/interop/index.html: $(call manual-deps,interop) $(SRC_PATH)/qga/qapi-schema.json $(qapi-modules) $(qapi-py)
 	$(call build-manual,interop,html)
 
 $(MANUAL_BUILDDIR)/specs/index.html: $(call manual-deps,specs)
@@ -1103,9 +1092,9 @@ $(MANUAL_BUILDDIR)/user/index.html: $(call manual-deps,user)
 	$(call build-manual,user,html)
 
 $(call define-manpage-rule,interop,\
-       qemu-ga.8 qemu-ga-ref.7,\
+       qemu-ga.8 qemu-ga-ref.7 qemu-qmp-ref.7,\
        $(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/qga/qapi-schema.json \
-       $(qapi-py))
+       $(qapi-modules) $(qapi-py))
 
 $(call define-manpage-rule,system,qemu.1 qemu-block-drivers.7 qemu-cpu-models.7)
 
@@ -1119,18 +1108,7 @@ $(MANUAL_BUILDDIR)/index.html: $(SRC_PATH)/docs/index.html.in qemu-version.h
 	$(call quiet-command, sed "s|@@VERSION@@|${VERSION}|g" $< >$@, \
              "GEN","$@")
 
-docs/interop/qemu-qmp-qapi.texi: qapi/qapi-doc.texi
-	@cp -p $< $@
-
-html: docs/interop/qemu-qmp-ref.html sphinxdocs
-info: docs/interop/qemu-qmp-ref.info
-pdf: docs/interop/qemu-qmp-ref.pdf
-txt: docs/interop/qemu-qmp-ref.txt
-
-docs/interop/qemu-qmp-ref.dvi docs/interop/qemu-qmp-ref.html \
-    docs/interop/qemu-qmp-ref.info docs/interop/qemu-qmp-ref.pdf \
-    docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7: \
-	docs/interop/qemu-qmp-ref.texi docs/interop/qemu-qmp-qapi.texi
+html: sphinxdocs
 
 $(filter %.1 %.7 %.8,$(DOCS)): scripts/texi2pod.pl
 
@@ -1271,7 +1249,7 @@ endif
 	$(call print-help,vm-help,Help about targets running tests inside VM)
 	@echo  ''
 	@echo  'Documentation targets:'
-	$(call print-help,html info pdf txt,Build documentation in specified format)
+	$(call print-help,html,Build HTML documentation)
 ifdef CONFIG_GCOV
 	$(call print-help,coverage-report,Create code coverage report)
 endif
-- 
2.20.1



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

* [PATCH v5 12/20] qapi: Use rST markup for literal blocks
  2020-08-10 19:49 [PATCH v5 00/20] Convert QAPI doc comments to generate rST instead of texinfo Peter Maydell
                   ` (10 preceding siblings ...)
  2020-08-10 19:50 ` [PATCH v5 11/20] docs/interop: Convert qemu-qmp-ref " Peter Maydell
@ 2020-08-10 19:50 ` Peter Maydell
  2020-09-04 13:02   ` Markus Armbruster
  2020-08-10 19:50 ` [PATCH v5 13/20] qga/qapi-schema.json: Add some headings Peter Maydell
                   ` (8 subsequent siblings)
  20 siblings, 1 reply; 62+ messages in thread
From: Peter Maydell @ 2020-08-10 19:50 UTC (permalink / raw)
  To: qemu-devel; +Cc: Markus Armbruster

There are exactly two places in our json doc comments where we
use the markup accepted by the texi doc generator where a '|' in
the first line of a doc comment means the line should be emitted
as a literal block (fixed-width font, whitespace preserved).

Since we use this syntax so rarely, instead of making the rST
generator support it, instead just convert the two uses to
rST-format literal blocks, which are indented and introduced
with '::'.

(The rST generator doesn't complain about the old style syntax,
it just emits it with the '|' and with the whitespace not
preserved, which looks odd, but means we can safely leave this
change until after we've stopped generating texinfo.)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 qapi/block-core.json  | 16 +++++++++-------
 qapi/qapi-schema.json |  6 ++++--
 2 files changed, 13 insertions(+), 9 deletions(-)

diff --git a/qapi/block-core.json b/qapi/block-core.json
index 535b2b2e7bf..12758116e85 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -566,13 +566,15 @@
 #        For the example above, @bins may be something like [3, 1, 5, 2],
 #        and corresponding histogram looks like:
 #
-# |      5|           *
-# |      4|           *
-# |      3| *         *
-# |      2| *         *    *
-# |      1| *    *    *    *
-# |       +------------------
-# |           10   50   100
+# ::
+#
+#        5|           *
+#        4|           *
+#        3| *         *
+#        2| *         *    *
+#        1| *    *    *    *
+#         +------------------
+#             10   50   100
 #
 # Since: 4.0
 ##
diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json
index 5fc0771eb04..c19b4267058 100644
--- a/qapi/qapi-schema.json
+++ b/qapi/qapi-schema.json
@@ -23,8 +23,10 @@
 #
 # Example:
 #
-# | -> data issued by the Client
-# | <- Server data response
+# ::
+#
+#   -> data issued by the Client
+#   <- Server data response
 #
 # Please, refer to the QMP specification (docs/interop/qmp-spec.txt) for
 # detailed information on the Server command and response formats.
-- 
2.20.1



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

* [PATCH v5 13/20] qga/qapi-schema.json: Add some headings
  2020-08-10 19:49 [PATCH v5 00/20] Convert QAPI doc comments to generate rST instead of texinfo Peter Maydell
                   ` (11 preceding siblings ...)
  2020-08-10 19:50 ` [PATCH v5 12/20] qapi: Use rST markup for literal blocks Peter Maydell
@ 2020-08-10 19:50 ` Peter Maydell
  2020-08-10 19:50 ` [PATCH v5 14/20] scripts/qapi: Remove texinfo generation support Peter Maydell
                   ` (7 subsequent siblings)
  20 siblings, 0 replies; 62+ messages in thread
From: Peter Maydell @ 2020-08-10 19:50 UTC (permalink / raw)
  To: qemu-devel; +Cc: Markus Armbruster

Add some section headings to the QGA json; this is purely so that we
have some H1 headings, as otherwise each command ends up being
visible in the interop/ manual's table of contents.  In an ideal
world there might be a proper 'Introduction' section the way there is
in qapi/qapi-schema.json.

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 qga/qapi-schema.json | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index b1e9ed836de..401467860a7 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -2,14 +2,18 @@
 # vim: filetype=python
 
 ##
-#
-# General note concerning the use of guest agent interfaces:
-#
+# = General note concerning the use of guest agent interfaces
+##
+
+##
 # "unsupported" is a higher-level error than the errors that individual
 # commands might document. The caller should always be prepared to receive
 # QERR_UNSUPPORTED, even if the given command doesn't specify it, or doesn't
 # document any failure mode at all.
-#
+##
+
+##
+# = QEMU guest agent protocol commands and structs
 ##
 
 { 'pragma': { 'doc-required': true } }
-- 
2.20.1



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

* [PATCH v5 14/20] scripts/qapi: Remove texinfo generation support
  2020-08-10 19:49 [PATCH v5 00/20] Convert QAPI doc comments to generate rST instead of texinfo Peter Maydell
                   ` (12 preceding siblings ...)
  2020-08-10 19:50 ` [PATCH v5 13/20] qga/qapi-schema.json: Add some headings Peter Maydell
@ 2020-08-10 19:50 ` Peter Maydell
  2020-09-04 13:37   ` Markus Armbruster
  2020-08-10 19:50 ` [PATCH v5 15/20] docs/devel/qapi-code-gen.txt: Update to new rST backend conventions Peter Maydell
                   ` (6 subsequent siblings)
  20 siblings, 1 reply; 62+ messages in thread
From: Peter Maydell @ 2020-08-10 19:50 UTC (permalink / raw)
  To: qemu-devel; +Cc: Markus Armbruster

We no longer use the generated texinfo format documentation,
so delete the code that generates it, and the test case for
the generation.

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 Makefile                        |   1 -
 scripts/qapi-gen.py             |   2 -
 scripts/qapi/doc.py             | 302 ------------------------------
 scripts/qapi/gen.py             |   7 -
 tests/Makefile.include          |  15 +-
 tests/qapi-schema/doc-good.texi | 313 --------------------------------
 6 files changed, 1 insertion(+), 639 deletions(-)
 delete mode 100644 scripts/qapi/doc.py
 delete mode 100644 tests/qapi-schema/doc-good.texi

diff --git a/Makefile b/Makefile
index 3df1cf68333..fc3ccc15030 100644
--- a/Makefile
+++ b/Makefile
@@ -626,7 +626,6 @@ qemu-keymap$(EXESUF): QEMU_CFLAGS += $(XKBCOMMON_CFLAGS)
 qapi-py = $(SRC_PATH)/scripts/qapi/__init__.py \
 $(SRC_PATH)/scripts/qapi/commands.py \
 $(SRC_PATH)/scripts/qapi/common.py \
-$(SRC_PATH)/scripts/qapi/doc.py \
 $(SRC_PATH)/scripts/qapi/error.py \
 $(SRC_PATH)/scripts/qapi/events.py \
 $(SRC_PATH)/scripts/qapi/expr.py \
diff --git a/scripts/qapi-gen.py b/scripts/qapi-gen.py
index 4b03f7d53be..541e8c1f55d 100755
--- a/scripts/qapi-gen.py
+++ b/scripts/qapi-gen.py
@@ -10,7 +10,6 @@ import re
 import sys
 
 from qapi.commands import gen_commands
-from qapi.doc import gen_doc
 from qapi.events import gen_events
 from qapi.introspect import gen_introspect
 from qapi.schema import QAPIError, QAPISchema
@@ -51,7 +50,6 @@ def main(argv):
     gen_commands(schema, args.output_dir, args.prefix)
     gen_events(schema, args.output_dir, args.prefix)
     gen_introspect(schema, args.output_dir, args.prefix, args.unmask)
-    gen_doc(schema, args.output_dir, args.prefix)
 
 
 if __name__ == '__main__':
diff --git a/scripts/qapi/doc.py b/scripts/qapi/doc.py
deleted file mode 100644
index 7764de1e4bc..00000000000
--- a/scripts/qapi/doc.py
+++ /dev/null
@@ -1,302 +0,0 @@
-# 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
-from qapi.gen import QAPIGenDoc, QAPISchemaVisitor
-
-
-MSG_FMT = """
-@deftypefn {type} {{}} {name}
-
-{body}{members}{features}{sections}
-@end deftypefn
-
-""".format
-
-TYPE_FMT = """
-@deftp {{{type}}} {name}
-
-{body}{members}{features}{sections}
-@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'@strong{\1}', doc)
-
-
-def subst_emph(doc):
-    """Replaces _foo_ by @emph{foo}"""
-    return re.sub(r'\b_([^_\n]+)_\b', 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"""
-    # TODO: Neglects to escape @ characters.
-    # We should probably escape them in subst_braces(), and rename the
-    # function to subst_special() or subs_texi_special().  If we do that, we
-    # need to delay it until after subst_vars() in texi_format().
-    doc = subst_braces(doc).strip('\n')
-    return EXAMPLE_FMT(code=doc)
-
-
-def texi_format(doc):
-    """
-    Format documentation
-
-    Lines starting with:
-    - |: generates an @example
-    - =: generates @section
-    - ==: generates @subsection
-    - 1. or 1): generates an @enumerate @item
-    - */-: generates an @itemize list
-    """
-    ret = ''
-    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'):
-        line = line.strip()
-        empty = line == ''
-
-        # FIXME: Doing this in a single if / elif chain is
-        # problematic.  For instance, a line without markup terminates
-        # a list if it follows a blank line (reaches the final elif),
-        # but a line with some *other* markup, such as a = title
-        # doesn't.
-        #
-        # Make sure to update section "Documentation markup" in
-        # docs/devel/qapi-code-gen.txt when fixing this.
-        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(r'^([0-9]*\.) ', line):
-            if not inlist:
-                ret += '@enumerate\n'
-                inlist = 'enumerate'
-            ret += '@item\n'
-            line = line[line.find(' ')+1:]
-        elif re.match(r'^[*-] ', line):
-            if not inlist:
-                ret += '@itemize %s\n' % {'*': '@bullet',
-                                          '-': '@minus'}[line[0]]
-                inlist = 'itemize'
-            ret += '@item\n'
-            line = line[2:]
-        elif lastempty and inlist:
-            ret += '@end %s\n\n' % inlist
-            inlist = ''
-
-        lastempty = empty
-        ret += line + '\n'
-
-    if inlist:
-        ret += '@end %s\n\n' % inlist
-    return ret
-
-
-def texi_body(doc):
-    """Format the main documentation body"""
-    return texi_format(doc.body.text)
-
-
-def texi_if(ifcond, prefix='\n', suffix='\n'):
-    """Format the #if condition"""
-    if not ifcond:
-        return ''
-    return '%s@b{If:} @code{%s}%s' % (prefix, ', '.join(ifcond), suffix)
-
-
-def texi_enum_value(value, desc, suffix):
-    """Format a table of members item for an enumeration value"""
-    return '@item @code{%s}\n%s%s' % (
-        value.name, desc, texi_if(value.ifcond, prefix='@*'))
-
-
-def texi_member(member, desc, suffix):
-    """Format a table of members item for an object type member"""
-    typ = member.type.doc_type()
-    membertype = ': ' + typ if typ else ''
-    return '@item @code{%s%s}%s%s\n%s%s' % (
-        member.name, membertype,
-        ' (optional)' if member.optional else '',
-        suffix, desc, texi_if(member.ifcond, prefix='@*'))
-
-
-def texi_members(doc, what, base=None, variants=None,
-                 member_func=texi_member):
-    """Format the table of members"""
-    items = ''
-    for section in doc.args.values():
-        # TODO Drop fallbacks when undocumented members are outlawed
-        if section.text:
-            desc = texi_format(section.text)
-        elif (variants and variants.tag_member == section.member
-              and not section.member.type.doc_type()):
-            values = section.member.type.member_names()
-            members_text = ', '.join(['@t{"%s"}' % v for v in values])
-            desc = 'One of ' + members_text + '\n'
-        else:
-            desc = 'Not documented\n'
-        items += member_func(section.member, desc, suffix='')
-    if base:
-        items += '@item The members of @code{%s}\n' % base.doc_type()
-    if variants:
-        for v in variants.variants:
-            when = ' when @code{%s} is @t{"%s"}%s' % (
-                variants.tag_member.name, v.name, texi_if(v.ifcond, " (", ")"))
-            if v.type.is_implicit():
-                assert not v.type.base and not v.type.variants
-                for m in v.type.local_members:
-                    items += member_func(m, desc='', suffix=when)
-            else:
-                items += '@item The members of @code{%s}%s\n' % (
-                    v.type.doc_type(), when)
-    if not items:
-        return ''
-    return '\n@b{%s:}\n@table @asis\n%s@end table\n' % (what, items)
-
-
-def texi_arguments(doc, boxed_arg_type):
-    if boxed_arg_type:
-        assert not doc.args
-        return ('\n@b{Arguments:} the members of @code{%s}\n'
-                % boxed_arg_type.name)
-    return texi_members(doc, 'Arguments')
-
-
-def texi_features(doc):
-    """Format the table of features"""
-    items = ''
-    for section in doc.features.values():
-        desc = texi_format(section.text)
-        items += '@item @code{%s}\n%s' % (section.name, desc)
-    if not items:
-        return ''
-    return '\n@b{Features:}\n@table @asis\n%s@end table\n' % (items)
-
-
-def texi_sections(doc, ifcond):
-    """Format additional sections following arguments"""
-    body = ''
-    for section in doc.sections:
-        if section.name:
-            # prefer @b over @strong, so txt doesn't translate it to *Foo:*
-            body += '\n@b{%s:}\n' % section.name
-        if section.name and section.name.startswith('Example'):
-            body += texi_example(section.text)
-        else:
-            body += texi_format(section.text)
-    body += texi_if(ifcond, suffix='')
-    return body
-
-
-def texi_type(typ, doc, ifcond, members):
-    return TYPE_FMT(type=typ,
-                    name=doc.symbol,
-                    body=texi_body(doc),
-                    members=members,
-                    features=texi_features(doc),
-                    sections=texi_sections(doc, ifcond))
-
-
-def texi_msg(typ, doc, ifcond, members):
-    return MSG_FMT(type=typ,
-                   name=doc.symbol,
-                   body=texi_body(doc),
-                   members=members,
-                   features=texi_features(doc),
-                   sections=texi_sections(doc, ifcond))
-
-
-class QAPISchemaGenDocVisitor(QAPISchemaVisitor):
-    def __init__(self, prefix):
-        self._prefix = prefix
-        self._gen = QAPIGenDoc(self._prefix + 'qapi-doc.texi')
-        self.cur_doc = None
-
-    def write(self, output_dir):
-        self._gen.write(output_dir)
-
-    def visit_enum_type(self, name, info, ifcond, features, members, prefix):
-        doc = self.cur_doc
-        self._gen.add(texi_type('Enum', doc, ifcond,
-                                texi_members(doc, 'Values',
-                                             member_func=texi_enum_value)))
-
-    def visit_object_type(self, name, info, ifcond, features,
-                          base, members, variants):
-        doc = self.cur_doc
-        if base and base.is_implicit():
-            base = None
-        self._gen.add(texi_type('Object', doc, ifcond,
-                                texi_members(doc, 'Members', base, variants)))
-
-    def visit_alternate_type(self, name, info, ifcond, features, variants):
-        doc = self.cur_doc
-        self._gen.add(texi_type('Alternate', doc, ifcond,
-                                texi_members(doc, 'Members')))
-
-    def visit_command(self, name, info, ifcond, features,
-                      arg_type, ret_type, gen, success_response, boxed,
-                      allow_oob, allow_preconfig):
-        doc = self.cur_doc
-        self._gen.add(texi_msg('Command', doc, ifcond,
-                               texi_arguments(doc,
-                                              arg_type if boxed else None)))
-
-    def visit_event(self, name, info, ifcond, features, arg_type, boxed):
-        doc = self.cur_doc
-        self._gen.add(texi_msg('Event', doc, ifcond,
-                               texi_arguments(doc,
-                                              arg_type if boxed else None)))
-
-    def symbol(self, doc, entity):
-        if self._gen._body:
-            self._gen.add('\n')
-        self.cur_doc = doc
-        entity.visit(self)
-        self.cur_doc = None
-
-    def freeform(self, doc):
-        assert not doc.args
-        if self._gen._body:
-            self._gen.add('\n')
-        self._gen.add(texi_body(doc) + texi_sections(doc, None))
-
-
-def gen_doc(schema, output_dir, prefix):
-    vis = QAPISchemaGenDocVisitor(prefix)
-    vis.visit_begin(schema)
-    for doc in schema.docs:
-        if doc.symbol:
-            vis.symbol(doc, schema.lookup_entity(doc.symbol))
-        else:
-            vis.freeform(doc)
-    vis.write(output_dir)
diff --git a/scripts/qapi/gen.py b/scripts/qapi/gen.py
index bf5552a4e7f..ca66c82b5b8 100644
--- a/scripts/qapi/gen.py
+++ b/scripts/qapi/gen.py
@@ -178,13 +178,6 @@ def ifcontext(ifcond, *args):
         arg.end_if()
 
 
-class QAPIGenDoc(QAPIGen):
-
-    def _top(self):
-        return (super()._top()
-                + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n')
-
-
 class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor):
 
     def __init__(self, prefix, what, blurb, pydoc):
diff --git a/tests/Makefile.include b/tests/Makefile.include
index c7e4646ded7..ec83efeaa63 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -38,7 +38,6 @@ export SRC_PATH
 qapi-py = $(SRC_PATH)/scripts/qapi/__init__.py \
 $(SRC_PATH)/scripts/qapi/commands.py \
 $(SRC_PATH)/scripts/qapi/common.py \
-$(SRC_PATH)/scripts/qapi/doc.py \
 $(SRC_PATH)/scripts/qapi/error.py \
 $(SRC_PATH)/scripts/qapi/events.py \
 $(SRC_PATH)/scripts/qapi/expr.py \
@@ -501,16 +500,8 @@ tests/test-qapi-gen-timestamp: \
 	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \
 		-o tests -p "test-" $<, \
 		"GEN","$(@:%-timestamp=%)")
-	@rm -f tests/test-qapi-doc.texi
 	@>$@
 
-tests/qapi-schema/doc-good.test.texi: $(SRC_PATH)/tests/qapi-schema/doc-good.json $(qapi-py)
-	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \
-		-o tests/qapi-schema -p "doc-good-" $<, \
-		"GEN","$@")
-	@mv tests/qapi-schema/doc-good-qapi-doc.texi $@
-	@rm -f tests/qapi-schema/doc-good-qapi-*.[ch] tests/qapi-schema/doc-good-qmp-*.[ch]
-
 tests/qtest/dbus-vmstate1.h tests/qtest/dbus-vmstate1.c: tests/qtest/dbus-vmstate1-gen-timestamp ;
 tests/qtest/dbus-vmstate1-gen-timestamp: $(SRC_PATH)/tests/qtest/dbus-vmstate1.xml
 	$(call quiet-command,$(GDBUS_CODEGEN) $< \
@@ -891,10 +882,6 @@ check-tests/qapi-schema/frontend: $(addprefix $(SRC_PATH)/, $(check-qapi-schema-
 	  PYTHONIOENCODING=utf-8 $(PYTHON) $(SRC_PATH)/tests/qapi-schema/test-qapi.py $^, \
 	  TEST, check-qapi-schema)
 
-.PHONY: check-tests/qapi-schema/doc-good.texi
-check-tests/qapi-schema/doc-good.texi: tests/qapi-schema/doc-good.test.texi
-	@diff -u $(SRC_PATH)/tests/qapi-schema/doc-good.texi $<
-
 .PHONY: check-decodetree
 check-decodetree:
 	$(call quiet-command, \
@@ -956,7 +943,7 @@ check-acceptance: check-venv $(TESTS_RESULTS_DIR) get-vm-images
 # Consolidated targets
 
 .PHONY: check-block check-qapi-schema check-qtest check-unit check check-clean get-vm-images
-check-qapi-schema: check-tests/qapi-schema/frontend check-tests/qapi-schema/doc-good.texi
+check-qapi-schema: check-tests/qapi-schema/frontend
 check-qtest: $(patsubst %,check-qtest-%, $(QTEST_TARGETS))
 ifeq ($(CONFIG_TOOLS),y)
 check-block: $(patsubst %,check-%, $(check-block-y))
diff --git a/tests/qapi-schema/doc-good.texi b/tests/qapi-schema/doc-good.texi
deleted file mode 100644
index 12808989ffb..00000000000
--- a/tests/qapi-schema/doc-good.texi
+++ /dev/null
@@ -1,313 +0,0 @@
-@c AUTOMATICALLY GENERATED, DO NOT MODIFY
-
-@section Section
-
-@subsection Subsection
-
-@strong{strong} @emph{with emphasis}
-@code{var} @{in braces@}
-
-@itemize @bullet
-@item
-List item one
-@item
-Two, multiple
-lines
-
-@item
-Three
-Still in list
-
-@end itemize
-
-Not in list
-
-@itemize @minus
-@item
-Second list
-Note: still in list
-
-@end itemize
-
-Note: not in list
-
-@enumerate
-@item
-Third list
-is numbered
-
-@item
-another item
-
-@end enumerate
-
-Returns: the King
-Since: the first age
-Notes:
-
-@enumerate
-@item
-Lorem ipsum dolor sit amet
-
-@item
-Ut enim ad minim veniam
-
-@end enumerate
-
-Duis aute irure dolor
-
-Example:
-
--> in
-<- out
-Examples:
-@itemize @minus
-@item
-@strong{verbatim}
-@item
-@{braces@}
-@end itemize
-
-
-
-@deftp {Enum} Enum
-
-
-
-@b{Values:}
-@table @asis
-@item @code{one}
-The @emph{one} @{and only@}
-@*@b{If:} @code{defined(IFONE)}
-@item @code{two}
-Not documented
-@end table
-
-@b{Features:}
-@table @asis
-@item @code{enum-feat}
-Also @emph{one} @{and only@}
-@end table
-@code{two} is undocumented
-
-@b{If:} @code{defined(IFCOND)}
-@end deftp
-
-
-
-@deftp {Object} Base
-
-
-
-@b{Members:}
-@table @asis
-@item @code{base1: Enum}
-the first member
-@end table
-
-@end deftp
-
-
-
-@deftp {Object} Variant1
-
-A paragraph
-
-Another paragraph (but no @code{var}: line)
-
-@b{Members:}
-@table @asis
-@item @code{var1: string}
-Not documented
-@*@b{If:} @code{defined(IFSTR)}
-@end table
-
-@b{Features:}
-@table @asis
-@item @code{variant1-feat}
-a feature
-@item @code{member-feat}
-a member feature
-@end table
-
-@end deftp
-
-
-
-@deftp {Object} Variant2
-
-
-
-@end deftp
-
-
-
-@deftp {Object} Object
-
-
-
-@b{Members:}
-@table @asis
-@item The members of @code{Base}
-@item The members of @code{Variant1} when @code{base1} is @t{"one"}
-@item The members of @code{Variant2} when @code{base1} is @t{"two"} (@b{If:} @code{IFTWO})
-@end table
-
-@b{Features:}
-@table @asis
-@item @code{union-feat1}
-a feature
-@end table
-
-@end deftp
-
-
-
-@deftp {Object} SugaredUnion
-
-
-
-@b{Members:}
-@table @asis
-@item @code{type}
-One of @t{"one"}, @t{"two"}
-@item @code{data: Variant1} when @code{type} is @t{"one"}
-@item @code{data: Variant2} when @code{type} is @t{"two"} (@b{If:} @code{IFTWO})
-@end table
-
-@b{Features:}
-@table @asis
-@item @code{union-feat2}
-a feature
-@end table
-
-@end deftp
-
-
-
-@deftp {Alternate} Alternate
-
-
-
-@b{Members:}
-@table @asis
-@item @code{i: int}
-an integer
-@code{b} is undocumented
-@item @code{b: boolean}
-Not documented
-@end table
-
-@b{Features:}
-@table @asis
-@item @code{alt-feat}
-a feature
-@end table
-
-@end deftp
-
-
-@subsection Another subsection
-
-
-@deftypefn Command {} cmd
-
-
-
-@b{Arguments:}
-@table @asis
-@item @code{arg1: int}
-the first argument
-@item @code{arg2: string} (optional)
-the second
-argument
-@item @code{arg3: boolean}
-Not documented
-@end table
-
-@b{Features:}
-@table @asis
-@item @code{cmd-feat1}
-a feature
-@item @code{cmd-feat2}
-another feature
-@end table
-
-@b{Note:}
-@code{arg3} is undocumented
-
-@b{Returns:}
-@code{Object}
-
-@b{TODO:}
-frobnicate
-
-@b{Notes:}
-@itemize @minus
-@item
-Lorem ipsum dolor sit amet
-@item
-Ut enim ad minim veniam
-
-@end itemize
-
-Duis aute irure dolor
-
-@b{Example:}
-@example
--> in
-<- out
-@end example
-
-@b{Examples:}
-@example
-- *verbatim*
-- @{braces@}
-@end example
-
-@b{Since:}
-2.10
-
-@end deftypefn
-
-
-
-@deftypefn Command {} cmd-boxed
-
-If you're bored enough to read this, go see a video of boxed cats
-
-@b{Arguments:} the members of @code{Object}
-
-@b{Features:}
-@table @asis
-@item @code{cmd-feat1}
-a feature
-@item @code{cmd-feat2}
-another feature
-@end table
-
-@b{Example:}
-@example
--> in
-
-<- out
-@end example
-
-@end deftypefn
-
-
-
-@deftypefn Event {} EVT-BOXED
-
-
-
-@b{Arguments:} the members of @code{Object}
-
-@b{Features:}
-@table @asis
-@item @code{feat3}
-a feature
-@end table
-
-@end deftypefn
-
-- 
2.20.1



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

* [PATCH v5 15/20] docs/devel/qapi-code-gen.txt: Update to new rST backend conventions
  2020-08-10 19:49 [PATCH v5 00/20] Convert QAPI doc comments to generate rST instead of texinfo Peter Maydell
                   ` (13 preceding siblings ...)
  2020-08-10 19:50 ` [PATCH v5 14/20] scripts/qapi: Remove texinfo generation support Peter Maydell
@ 2020-08-10 19:50 ` Peter Maydell
  2020-09-17  9:24   ` Markus Armbruster
  2020-08-10 19:50 ` [PATCH v5 16/20] Makefile: Remove redundant Texinfo related rules Peter Maydell
                   ` (5 subsequent siblings)
  20 siblings, 1 reply; 62+ messages in thread
From: Peter Maydell @ 2020-08-10 19:50 UTC (permalink / raw)
  To: qemu-devel; +Cc: Markus Armbruster

Update the documentation of QAPI document comment syntax to match
the new rST backend requirements. The principal changes are:
 * whitespace is now significant, and multiline definitions
   must have their second and subsequent lines indented to
   match the first line
 * general rST format markup is permitted, not just the small
   set of markup the old texinfo generator handled. For most
   things (notably bulleted and itemized lists) the old format
   is the same as rST was.
 * Specific things that might trip people up:
   - instead of *bold* and _italic_ rST has **bold** and *italic*
   - lists need a preceding and following blank line
   - a lone literal '*' will need to be backslash-escaped to
     avoid a rST syntax error
 * the old leading '|' for example (literal text) blocks is
   replaced by the standard rST '::' literal block.
 * headings and subheadings must now be in a freeform
   documentation comment of their own
 * we support arbitrary levels of sub- and sub-sub-heading, not
   just a main and sub-heading like the old texinfo generator

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 docs/devel/qapi-code-gen.txt | 90 ++++++++++++++++++++++++------------
 1 file changed, 61 insertions(+), 29 deletions(-)

diff --git a/docs/devel/qapi-code-gen.txt b/docs/devel/qapi-code-gen.txt
index 69eede6c283..263f1c0b44c 100644
--- a/docs/devel/qapi-code-gen.txt
+++ b/docs/devel/qapi-code-gen.txt
@@ -824,21 +824,39 @@ See below for more on definition documentation.
 Free-form documentation may be used to provide additional text and
 structuring content.
 
+==== Headings and subheadings ====
+
+A free-form documentation comment containing a single line
+which starts with some '=' symbols and then a space defines
+a section heading:
+
+    ##
+    # = This is a top level heading
+    ##
+
+    ##
+    # This is a free-form comment which will go under the
+    # top level heading.
+    ##
+
+    ##
+    # == This is a second level heading
+    ##
+
+Section headings must always be correctly nested, so you can only
+define a third-level heading inside a second-level heading, and so
+on. The documentation generator will catch nesting mistakes and report
+a syntax error.
 
 ==== Documentation markup ====
 
-Comment text starting with '=' is a section title:
+Documentation comments can use most rST markup. In particular,
+a '::' literal block can be used for examples:
 
-    # = Section title
-
-Double the '=' for a subsection title:
-
-    # == Subsection title
-
-'|' denotes examples:
-
-    # | Text of the example, may span
-    # | multiple lines
+    # ::
+    #
+    #   Text of the example, may span
+    #   multiple lines
 
 '*' starts an itemized list:
 
@@ -854,37 +872,35 @@ A decimal number followed by '.' starts a numbered list:
     #    multiple lines
     # 2. Second item
 
-The actual number doesn't matter.  You could even use '*' instead of
-'2.' for the second item.
+The actual number doesn't matter.
 
-Lists can't be nested.  Blank lines are currently not supported within
-lists.
+Lists of either kind must be preceded and followed by a blank line.
+If a list item's text spans multiple lines, then the second and
+subsequent lines must be correctly indented to line up with the
+first character of the first line.
 
-Additional whitespace between the initial '#' and the comment text is
-permitted.
-
-*foo* and _foo_ are for strong and emphasis styles respectively (they
-do not work over multiple lines).  @foo is used to reference a name in
-the schema.
+The usual '**strong**', '*emphasised*' and '``literal``' markup should
+be used. If you need a single literal '*' you will need to backslash-escape it.
+As an extension beyond the usual rST syntax, you can also
+use '@foo' to reference a name in the schema; this is rendered
+the same way as '``foo``'.
 
 Example:
 
 ##
-# = Section
-# == Subsection
-#
-# Some text foo with *strong* and _emphasis_
+# Some text foo with **bol** and *emphasis*
 # 1. with a list
 # 2. like that
 #
 # And some code:
-# | $ echo foo
-# | -> do this
-# | <- get that
 #
+# ::
+#
+#   $ echo foo
+#   -> do this
+#   <- get that
 ##
 
-
 ==== Definition documentation ====
 
 Definition documentation, if present, must immediately precede the
@@ -899,6 +915,12 @@ commands and events), member (for structs and unions), branch (for
 alternates), or value (for enums), and finally optional tagged
 sections.
 
+Descriptions of arguments can span multiple lines; if they
+do then the second and subsequent lines must be indented
+to line up with the first character of the first line of the
+description. The parser will report a syntax error if there
+is insufficient indentation.
+
 FIXME: the parser accepts these things in almost any order.
 FIXME: union branches should be described, too.
 
@@ -912,6 +934,16 @@ The section ends with the start of a new section.
 A 'Since: x.y.z' tagged section lists the release that introduced the
 definition.
 
+The text of a section can start on a new line, in
+which case it must not be indented at all. It can also start
+on the same line as the 'Note:', 'Returns:', etc tag. In this
+case if it spans multiple lines then second and subsequent
+lines must be indented to match the first.
+
+An 'Example' or 'Examples' section is automatically rendered
+entirely as literal fixed-width text. In other sections,
+the text is formatted, and rST markup can be used.
+
 For example:
 
 ##
-- 
2.20.1



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

* [PATCH v5 16/20] Makefile: Remove redundant Texinfo related rules
  2020-08-10 19:49 [PATCH v5 00/20] Convert QAPI doc comments to generate rST instead of texinfo Peter Maydell
                   ` (14 preceding siblings ...)
  2020-08-10 19:50 ` [PATCH v5 15/20] docs/devel/qapi-code-gen.txt: Update to new rST backend conventions Peter Maydell
@ 2020-08-10 19:50 ` Peter Maydell
  2020-08-10 19:50 ` [PATCH v5 17/20] scripts/texi2pod: Delete unused script Peter Maydell
                   ` (4 subsequent siblings)
  20 siblings, 0 replies; 62+ messages in thread
From: Peter Maydell @ 2020-08-10 19:50 UTC (permalink / raw)
  To: qemu-devel; +Cc: Markus Armbruster

We now don't build anything from Texinfo, so we can remove
some redundant Makefile pattern rules and the rule for
generating the version.texi file that used to be included
from many Texinfo source files.

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 Makefile  | 31 +------------------------------
 rules.mak | 14 +-------------
 2 files changed, 2 insertions(+), 43 deletions(-)

diff --git a/Makefile b/Makefile
index fc3ccc15030..1bc0f4f1d5c 100644
--- a/Makefile
+++ b/Makefile
@@ -759,8 +759,7 @@ clean: recurse-clean
 		! -path ./roms/edk2/BaseTools/Source/Python/UPT/Dll/sqlite3.dll \
 		-exec rm {} +
 	rm -f $(edk2-decompressed)
-	rm -f $(filter-out %.tlb,$(TOOLS)) $(HELPERS-y) TAGS cscope.* *.pod *~ */*~
-	rm -f fsdev/*.pod scsi/*.pod
+	rm -f $(filter-out %.tlb,$(TOOLS)) $(HELPERS-y) TAGS cscope.* *~ */*~
 	rm -f qemu-img-cmds.h
 	rm -f ui/shader/*-vert.h ui/shader/*-frag.h
 	@# May not be present in generated-files-y
@@ -795,7 +794,6 @@ distclean: clean
 	rm -f qemu-plugins-ld.symbols qemu-plugins-ld64.symbols
 	rm -f config.log
 	rm -f linux-headers/asm
-	rm -f docs/version.texi
 	rm -rf .doctrees
 	$(call clean-manual,devel)
 	$(call clean-manual,interop)
@@ -1015,31 +1013,6 @@ ui/shader.o: $(SRC_PATH)/ui/shader.c \
 	ui/shader/texture-blit-frag.h
 
 # documentation
-MAKEINFO=makeinfo
-MAKEINFOINCLUDES= -I docs -I $(<D) -I $(@D)
-MAKEINFOFLAGS=--no-split --number-sections $(MAKEINFOINCLUDES)
-TEXI2PODFLAGS=$(MAKEINFOINCLUDES) -DVERSION="$(VERSION)" -DCONFDIR="$(qemu_confdir)"
-TEXI2PDFFLAGS=$(if $(V),,--quiet) -I $(SRC_PATH) $(MAKEINFOINCLUDES)
-
-docs/version.texi: $(SRC_PATH)/VERSION config-host.mak
-	$(call quiet-command,(\
-		echo "@set VERSION $(VERSION)" && \
-		echo "@set CONFDIR $(qemu_confdir)" \
-	)> $@,"GEN","$@")
-
-%.html: %.texi docs/version.texi
-	$(call quiet-command,LC_ALL=C $(MAKEINFO) $(MAKEINFOFLAGS) --no-headers \
-	--html $< -o $@,"GEN","$@")
-
-%.info: %.texi docs/version.texi
-	$(call quiet-command,$(MAKEINFO) $(MAKEINFOFLAGS) $< -o $@,"GEN","$@")
-
-%.txt: %.texi docs/version.texi
-	$(call quiet-command,LC_ALL=C $(MAKEINFO) $(MAKEINFOFLAGS) --no-headers \
-	--plaintext $< -o $@,"GEN","$@")
-
-%.pdf: %.texi docs/version.texi
-	$(call quiet-command,texi2pdf $(TEXI2PDFFLAGS) $< -o $@,"GEN","$@")
 
 # Sphinx builds all its documentation at once in one invocation
 # and handles "don't rebuild things unless necessary" itself.
@@ -1109,8 +1082,6 @@ $(MANUAL_BUILDDIR)/index.html: $(SRC_PATH)/docs/index.html.in qemu-version.h
 
 html: sphinxdocs
 
-$(filter %.1 %.7 %.8,$(DOCS)): scripts/texi2pod.pl
-
 # Reports/Analysis
 
 %/coverage-report.html:
diff --git a/rules.mak b/rules.mak
index 694865b63ee..1dc2a353ee6 100644
--- a/rules.mak
+++ b/rules.mak
@@ -144,7 +144,7 @@ cc-option = $(if $(shell $(CC) $1 $2 -S -o /dev/null -xc /dev/null \
 cc-c-option = $(if $(shell $(CC) $1 $2 -c -o /dev/null -xc /dev/null \
                 >/dev/null 2>&1 && echo OK), $2, $3)
 
-VPATH_SUFFIXES = %.c %.h %.S %.cc %.cpp %.m %.mak %.texi %.sh %.rc Kconfig% %.json.in
+VPATH_SUFFIXES = %.c %.h %.S %.cc %.cpp %.m %.mak %.sh %.rc Kconfig% %.json.in
 set-vpath = $(if $1,$(foreach PATTERN,$(VPATH_SUFFIXES),$(eval vpath $(PATTERN) $1)))
 
 # install-prog list, dir
@@ -381,18 +381,6 @@ define unnest-vars
         $(eval $v := $(filter-out %/,$($v))))
 endef
 
-TEXI2MAN = $(call quiet-command, \
-	perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $(TEXI2PODFLAGS) $< $@.pod && \
-	$(POD2MAN) --section=$(subst .,,$(suffix $@)) --center=" " --release=" " $@.pod > $@, \
-	"GEN","$@")
-
-%.1:
-	$(call TEXI2MAN)
-%.7:
-	$(call TEXI2MAN)
-%.8:
-	$(call TEXI2MAN)
-
 GEN_SUBST = $(call quiet-command, \
 	sed -e "s!@libexecdir@!$(libexecdir)!g" < $< > $@, \
 	"GEN","$@")
-- 
2.20.1



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

* [PATCH v5 17/20] scripts/texi2pod: Delete unused script
  2020-08-10 19:49 [PATCH v5 00/20] Convert QAPI doc comments to generate rST instead of texinfo Peter Maydell
                   ` (15 preceding siblings ...)
  2020-08-10 19:50 ` [PATCH v5 16/20] Makefile: Remove redundant Texinfo related rules Peter Maydell
@ 2020-08-10 19:50 ` Peter Maydell
  2020-08-10 19:50 ` [PATCH v5 18/20] Remove Texinfo related files from .gitignore and git.orderfile Peter Maydell
                   ` (3 subsequent siblings)
  20 siblings, 0 replies; 62+ messages in thread
From: Peter Maydell @ 2020-08-10 19:50 UTC (permalink / raw)
  To: qemu-devel; +Cc: Markus Armbruster

We no longer need the texi2pod script, so we can delete it, and
the special-casing it had in the checkpatch script.

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 scripts/checkpatch.pl |   2 +-
 scripts/texi2pod.pl   | 536 ------------------------------------------
 2 files changed, 1 insertion(+), 537 deletions(-)
 delete mode 100755 scripts/texi2pod.pl

diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index bd3faa154c3..b9935e0b650 100755
--- a/scripts/checkpatch.pl
+++ b/scripts/checkpatch.pl
@@ -1659,7 +1659,7 @@ sub process {
 # tabs are only allowed in assembly source code, and in
 # some scripts we imported from other projects.
 		next if ($realfile =~ /\.(s|S)$/);
-		next if ($realfile =~ /(checkpatch|get_maintainer|texi2pod)\.pl$/);
+		next if ($realfile =~ /(checkpatch|get_maintainer)\.pl$/);
 
 		if ($rawline =~ /^\+.*\t/) {
 			my $herevet = "$here\n" . cat_vet($rawline) . "\n";
diff --git a/scripts/texi2pod.pl b/scripts/texi2pod.pl
deleted file mode 100755
index 8bfc6f6f4c4..00000000000
--- a/scripts/texi2pod.pl
+++ /dev/null
@@ -1,536 +0,0 @@
-#! /usr/bin/env perl
-
-#   Copyright (C) 1999, 2000, 2001, 2003 Free Software Foundation, Inc.
-
-# This file is part of GCC.
-
-# GCC is free software; 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, or (at your option)
-# any later version.
-
-# GCC 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 GCC; see the file COPYING.  If not,
-# see <http://www.gnu.org/licenses/>.
-
-# This does trivial (and I mean _trivial_) conversion of Texinfo
-# markup to Perl POD format.  It's intended to be used to extract
-# something suitable for a manpage from a Texinfo document.
-
-use warnings;
-
-$output = 0;
-$skipping = 0;
-%sects = ();
-$section = "";
-@icstack = ();
-@endwstack = ();
-@skstack = ();
-@instack = ();
-$shift = "";
-%defs = ();
-$fnno = 1;
-$inf = "";
-$ibase = "";
-@ipath = ();
-$encoding = undef;
-@args = ();
-
-while ($_ = shift) {
-    if (/^-D(.*)$/) {
-	if ($1 ne "") {
-	    $flag = $1;
-	} else {
-	    $flag = shift;
-	}
-	$value = "";
-	($flag, $value) = ($flag =~ /^([^=]+)(?:=(.+))?/);
-	die "no flag specified for -D\n"
-	    unless $flag ne "";
-	die "flags may only contain letters, digits, hyphens, dashes and underscores\n"
-	    unless $flag =~ /^[a-zA-Z0-9_-]+$/;
-	$defs{$flag} = $value;
-    } elsif (/^-I(.*)$/) {
-	if ($1 ne "") {
-	    $flag = $1;
-	} else {
-	    $flag = shift;
-	}
-        push (@ipath, $flag);
-    } elsif (/^-/) {
-	usage();
-    } else {
-	$in = $_, next unless defined $in;
-	$out = $_, next unless defined $out;
-	usage();
-    }
-}
-
-if (defined $in) {
-    $inf = gensym();
-    open($inf, "<$in") or die "opening \"$in\": $!\n";
-    $ibase = $1 if $in =~ m|^(.+)/[^/]+$|;
-} else {
-    $inf = \*STDIN;
-}
-
-if (defined $out) {
-    open(STDOUT, ">$out") or die "opening \"$out\": $!\n";
-}
-
-while(defined $inf) {
-while(<$inf>) {
-    # Certain commands are discarded without further processing.
-    /^\@(?:
-	 [a-z]+index		# @*index: useful only in complete manual
-	 |need			# @need: useful only in printed manual
-	 |(?:end\s+)?group	# @group .. @end group: ditto
-	 |page			# @page: ditto
-	 |node			# @node: useful only in .info file
-	 |(?:end\s+)?ifnottex   # @ifnottex .. @end ifnottex: use contents
-	)\b/x and next;
-
-    chomp;
-
-    # Look for filename and title markers.
-    /^\@setfilename\s+([^.]+)/ and $fn = $1, next;
-    /^\@settitle\s+([^.]+)/ and $tl = postprocess($1), next;
-
-    # Look for document encoding
-    /^\@documentencoding\s+([^.]+)/ and do {
-        $encoding = $1 unless defined $encoding;
-        next;
-    };
-
-    # Identify a man title but keep only the one we are interested in.
-    /^\@c\s+man\s+title\s+([A-Za-z0-9-]+)\s+(.+)/ and do {
-	if (exists $defs{$1}) {
-	    $fn = $1;
-	    $tl = postprocess($2);
-	}
-	next;
-    };
-
-    # Look for blocks surrounded by @c man begin SECTION ... @c man end.
-    # This really oughta be @ifman ... @end ifman and the like, but such
-    # would require rev'ing all other Texinfo translators.
-    /^\@c\s+man\s+begin\s+([A-Z]+)\s+([A-Za-z0-9-]+)/ and do {
-	$output = 1 if exists $defs{$2};
-        $sect = $1;
-	next;
-    };
-    /^\@c\s+man\s+begin\s+([A-Z]+)/ and $sect = $1, $output = 1, next;
-    /^\@c\s+man\s+end/ and do {
-	$sects{$sect} = "" unless exists $sects{$sect};
-	$sects{$sect} .= postprocess($section);
-	$section = "";
-	$output = 0;
-	next;
-    };
-
-    # handle variables
-    /^\@set\s+([a-zA-Z0-9_-]+)\s*(.*)$/ and do {
-	$defs{$1} = $2;
-	next;
-    };
-    /^\@clear\s+([a-zA-Z0-9_-]+)/ and do {
-	delete $defs{$1};
-	next;
-    };
-
-    # Single line command handlers.
-
-    /^\@include\s+(.+)$/ and do {
-	push @instack, $inf;
-	$inf = gensym();
-	$file = postprocess($1);
-
-	# Try cwd and $ibase, then explicit -I paths.
-	$done = 0;
-	foreach $path ("", $ibase, @ipath) {
-	    $mypath = $file;
-	    $mypath = $path . "/" . $mypath if ($path ne "");
-	    open($inf, "<" . $mypath) and ($done = 1, last);
-	}
-	die "cannot find $file" if !$done;
-	next;
-    };
-
-    next unless $output;
-
-    # Discard comments.  (Can't do it above, because then we'd never see
-    # @c man lines.)
-    /^\@c\b/ and next;
-
-    # End-block handler goes up here because it needs to operate even
-    # if we are skipping.
-    /^\@end\s+([a-z]+)/ and do {
-	# Ignore @end foo, where foo is not an operation which may
-	# cause us to skip, if we are presently skipping.
-	my $ended = $1;
-	next if $skipping && $ended !~ /^(?:ifset|ifclear|ignore|menu|iftex|copying)$/;
-
-	die "\@end $ended without \@$ended at line $.\n" unless defined $endw;
-	die "\@$endw ended by \@end $ended at line $.\n" unless $ended eq $endw;
-
-	$endw = pop @endwstack;
-
-	if ($ended =~ /^(?:ifset|ifclear|ignore|menu|iftex)$/) {
-	    $skipping = pop @skstack;
-	    next;
-	} elsif ($ended =~ /^(?:example|smallexample|display
-                            |quotation|deftp|deftypefn)$/x) {
-	    $shift = "";
-	    $_ = "";	# need a paragraph break
-	} elsif ($ended =~ /^(?:itemize|enumerate|[fv]?table)$/) {
-	    $_ = "\n=back\n";
-	    $ic = pop @icstack;
-	} elsif ($ended eq "multitable") {
-	    $_ = "\n=back\n";
-	} else {
-	    die "unknown command \@end $ended at line $.\n";
-	}
-    };
-
-    # We must handle commands which can cause skipping even while we
-    # are skipping, otherwise we will not process nested conditionals
-    # correctly.
-    /^\@ifset\s+([a-zA-Z0-9_-]+)/ and do {
-	push @endwstack, $endw;
-	push @skstack, $skipping;
-	$endw = "ifset";
-	$skipping = 1 unless exists $defs{$1};
-	next;
-    };
-
-    /^\@ifclear\s+([a-zA-Z0-9_-]+)/ and do {
-	push @endwstack, $endw;
-	push @skstack, $skipping;
-	$endw = "ifclear";
-	$skipping = 1 if exists $defs{$1};
-	next;
-    };
-
-    /^\@(ignore|menu|iftex|copying)\b/ and do {
-	push @endwstack, $endw;
-	push @skstack, $skipping;
-	$endw = $1;
-	$skipping = 1;
-	next;
-    };
-
-    next if $skipping;
-
-    # Character entities.  First the ones that can be replaced by raw text
-    # or discarded outright:
-    s/\@copyright\{\}/(c)/g;
-    s/\@dots\{\}/.../g;
-    s/\@enddots\{\}/..../g;
-    s/\@([.!? ])/$1/g;
-    s/\@[:-]//g;
-    s/\@bullet(?:\{\})?/*/g;
-    s/\@TeX\{\}/TeX/g;
-    s/\@pounds\{\}/\#/g;
-    s/\@minus(?:\{\})?/-/g;
-    s/\\,/,/g;
-
-    # Now the ones that have to be replaced by special escapes
-    # (which will be turned back into text by unmunge())
-    s/&/&amp;/g;
-    s/\@\{/&lbrace;/g;
-    s/\@\}/&rbrace;/g;
-    s/\@\@/&at;/g;
-
-    # Inside a verbatim block, handle @var specially.
-    if ($shift ne "") {
-	s/\@var\{([^\}]*)\}/<$1>/g;
-    }
-
-    # POD doesn't interpret E<> inside a verbatim block.
-    if ($shift eq "") {
-	s/</&lt;/g;
-	s/>/&gt;/g;
-    } else {
-	s/</&LT;/g;
-	s/>/&GT;/g;
-    }
-
-    /^\@(?:section|unnumbered|unnumberedsec|center)\s+(.+)$/
-	and $_ = "\n=head2 $1\n";
-    /^\@subsection\s+(.+)$/
-	and $_ = "\n=head3 $1\n";
-    /^\@subsubsection\s+(.+)$/
-	and $_ = "\n=head4 $1\n";
-
-    # Block command handlers:
-    /^\@itemize(?:\s+(\@[a-z]+|\*|-))?/ and do {
-	push @endwstack, $endw;
-	push @icstack, $ic;
-	if (defined $1) {
-	    $ic = $1;
-	} else {
-	    $ic = '*';
-	}
-	$_ = "\n=over 4\n";
-	$endw = "itemize";
-    };
-
-    /^\@enumerate(?:\s+([a-zA-Z0-9]+))?/ and do {
-	push @endwstack, $endw;
-	push @icstack, $ic;
-	if (defined $1) {
-	    $ic = $1 . ".";
-	} else {
-	    $ic = "1.";
-	}
-	$_ = "\n=over 4\n";
-	$endw = "enumerate";
-    };
-
-    /^\@multitable\s.*/ and do {
-	push @endwstack, $endw;
-	$endw = "multitable";
-	$_ = "\n=over 4\n";
-    };
-
-    /^\@([fv]?table)\s+(\@[a-z]+)/ and do {
-	push @endwstack, $endw;
-	push @icstack, $ic;
-	$endw = $1;
-	$ic = $2;
-	$ic =~ s/\@(?:samp|strong|key|gcctabopt|option|env)/B/;
-	$ic =~ s/\@(?:code|kbd)/C/;
-	$ic =~ s/\@(?:dfn|var|emph|cite|i)/I/;
-	$ic =~ s/\@(?:file)/F/;
-	$ic =~ s/\@(?:asis)//;
-	$_ = "\n=over 4\n";
-    };
-
-    /^\@((?:small)?example|display)/ and do {
-	push @endwstack, $endw;
-	$endw = $1;
-	$shift = "\t";
-	$_ = "";	# need a paragraph break
-    };
-
-    /^\@item\s+(.*\S)\s*$/ and $endw eq "multitable" and do {
-	@columns = ();
-	for $column (split (/\s*\@tab\s*/, $1)) {
-	    # @strong{...} is used a @headitem work-alike
-	    $column =~ s/^\@strong\{(.*)\}$/$1/;
-	    push @columns, $column;
-	}
-	$_ = "\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) {
-            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/;
-	    $ic =~ s/(\d+)/$1 + 1/eg;
-	}
-    };
-
-    $section .= $shift.$_."\n";
-}
-# End of current file.
-close($inf);
-$inf = pop @instack;
-}
-
-die "No filename or title\n" unless defined $fn && defined $tl;
-
-print "=encoding $encoding\n\n" if defined $encoding;
-
-$sects{NAME} = "$fn \- $tl\n";
-$sects{FOOTNOTES} .= "=back\n" if exists $sects{FOOTNOTES};
-
-for $sect (qw(NAME SYNOPSIS DESCRIPTION OPTIONS ENVIRONMENT FILES
-	      BUGS NOTES FOOTNOTES EXAMPLES SEEALSO AUTHOR COPYRIGHT)) {
-    if(exists $sects{$sect}) {
-	$head = $sect;
-	$head =~ s/SEEALSO/SEE ALSO/;
-	print "=head1 $head\n\n";
-	print scalar unmunge ($sects{$sect});
-	print "\n";
-    }
-}
-
-sub usage
-{
-    die "usage: $0 [-D toggle...] [infile [outfile]]\n";
-}
-
-sub postprocess
-{
-    local $_ = $_[0];
-
-    # @value{foo} is replaced by whatever 'foo' is defined as.
-    while (m/(\@value\{([a-zA-Z0-9_-]+)\})/g) {
-	if (! exists $defs{$2}) {
-	    print STDERR "Option $2 not defined\n";
-	    s/\Q$1\E//;
-	} else {
-	    $value = $defs{$2};
-	    s/\Q$1\E/$value/;
-	}
-    }
-
-    # Formatting commands.
-    # Temporary escape for @r.
-    s/\@r\{([^\}]*)\}/R<$1>/g;
-    s/\@(?:dfn|var|emph|cite|i)\{([^\}]*)\}/I<$1>/g;
-    s/\@(?:code|kbd)\{([^\}]*)\}/C<$1>/g;
-    s/\@(?:gccoptlist|samp|strong|key|option|env|command|b)\{([^\}]*)\}/B<$1>/g;
-    s/\@sc\{([^\}]*)\}/\U$1/g;
-    s/\@file\{([^\}]*)\}/F<$1>/g;
-    s/\@w\{([^\}]*)\}/S<$1>/g;
-    s/\@t\{([^\}]*)\}/$1/g;
-    s/\@(?:dmn|math)\{([^\}]*)\}/$1/g;
-
-    # keep references of the form @ref{...}, print them bold
-    s/\@(?:ref)\{([^\}]*)\}/B<$1>/g;
-
-    # Change double single quotes to double quotes.
-    s/''/"/g;
-    s/``/"/g;
-
-    # Cross references are thrown away, as are @noindent and @refill.
-    # (@noindent is impossible in .pod, and @refill is unnecessary.)
-    # @* is also impossible in .pod; we discard it and any newline that
-    # follows it.  Similarly, our macro @gol must be discarded.
-
-    s/\(?\@xref\{(?:[^\}]*)\}(?:[^.<]|(?:<[^<>]*>))*\.\)?//g;
-    s/\s+\(\@pxref\{(?:[^\}]*)\}\)//g;
-    s/;\s+\@pxref\{(?:[^\}]*)\}//g;
-    s/\@noindent\s*//g;
-    s/\@refill//g;
-    s/\@gol//g;
-    s/\@\*\s*\n?//g;
-
-    # Anchors are thrown away
-    s/\@anchor\{(?:[^\}]*)\}//g;
-
-    # @uref can take one, two, or three arguments, with different
-    # semantics each time.  @url and @email are just like @uref with
-    # one argument, for our purposes.
-    s/\@(?:uref|url|email)\{([^\},]*)\}/&lt;B<$1>&gt;/g;
-    s/\@uref\{([^\},]*),([^\},]*)\}/$2 (C<$1>)/g;
-    s/\@uref\{([^\},]*),([^\},]*),([^\},]*)\}/$3/g;
-
-    # Un-escape <> at this point.
-    s/&LT;/</g;
-    s/&GT;/>/g;
-
-    # Now un-nest all B<>, I<>, R<>.  Theoretically we could have
-    # indefinitely deep nesting; in practice, one level suffices.
-    1 while s/([BIR])<([^<>]*)([BIR])<([^<>]*)>/$1<$2>$3<$4>$1</g;
-
-    # Replace R<...> with bare ...; eliminate empty markup, B<>;
-    # shift white space at the ends of [BI]<...> expressions outside
-    # the expression.
-    s/R<([^<>]*)>/$1/g;
-    s/[BI]<>//g;
-    s/([BI])<(\s+)([^>]+)>/$2$1<$3>/g;
-    s/([BI])<([^>]+?)(\s+)>/$1<$2>$3/g;
-
-    # Extract footnotes.  This has to be done after all other
-    # processing because otherwise the regexp will choke on formatting
-    # inside @footnote.
-    while (/\@footnote/g) {
-	s/\@footnote\{([^\}]+)\}/[$fnno]/;
-	add_footnote($1, $fnno);
-	$fnno++;
-    }
-
-    return $_;
-}
-
-sub unmunge
-{
-    # Replace escaped symbols with their equivalents.
-    local $_ = $_[0];
-
-    s/&lt;/E<lt>/g;
-    s/&gt;/E<gt>/g;
-    s/&lbrace;/\{/g;
-    s/&rbrace;/\}/g;
-    s/&at;/\@/g;
-    s/&amp;/&/g;
-    return $_;
-}
-
-sub add_footnote
-{
-    unless (exists $sects{FOOTNOTES}) {
-	$sects{FOOTNOTES} = "\n=over 4\n\n";
-    }
-
-    $sects{FOOTNOTES} .= "=item $fnno.\n\n"; $fnno++;
-    $sects{FOOTNOTES} .= $_[0];
-    $sects{FOOTNOTES} .= "\n\n";
-}
-
-# stolen from Symbol.pm
-{
-    my $genseq = 0;
-    sub gensym
-    {
-	my $name = "GEN" . $genseq++;
-	my $ref = \*{$name};
-	delete $::{$name};
-	return $ref;
-    }
-}
-- 
2.20.1



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

* [PATCH v5 18/20] Remove Texinfo related files from .gitignore and git.orderfile
  2020-08-10 19:49 [PATCH v5 00/20] Convert QAPI doc comments to generate rST instead of texinfo Peter Maydell
                   ` (16 preceding siblings ...)
  2020-08-10 19:50 ` [PATCH v5 17/20] scripts/texi2pod: Delete unused script Peter Maydell
@ 2020-08-10 19:50 ` Peter Maydell
  2020-08-10 19:50 ` [PATCH v5 19/20] configure: Drop texinfo requirement Peter Maydell
                   ` (2 subsequent siblings)
  20 siblings, 0 replies; 62+ messages in thread
From: Peter Maydell @ 2020-08-10 19:50 UTC (permalink / raw)
  To: qemu-devel; +Cc: Markus Armbruster

We don't use Texinfo any more; we can remove the references to the
.texi source files and the generated output files from our
.gitignore and git.orderfile.

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 .gitignore            | 15 ---------------
 scripts/git.orderfile |  1 -
 2 files changed, 16 deletions(-)

diff --git a/.gitignore b/.gitignore
index 2992d15931a..6a3e335f6a7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -45,20 +45,15 @@
 /qapi/qapi-visit-*.[ch]
 !/qapi/qapi-visit-core.c
 **/qapi/qapi-visit.[ch]
-**/qapi/qapi-doc.texi
 /qemu-edid
 /qemu-img
 /qemu-nbd
 /qemu-options.def
-/qemu-options.texi
-/qemu-img-cmds.texi
 /qemu-img-cmds.h
 /qemu-io
 /qemu-ga
 /qemu-bridge-helper
 /qemu-keymap
-/qemu-monitor.texi
-/qemu-monitor-info.texi
 /qemu-storage-daemon
 /qemu-version.h
 /qemu-version.h.tmp
@@ -83,7 +78,6 @@
 *.ky
 *.log
 *.pdf
-*.pod
 *.cps
 *.fns
 *.kys
@@ -126,15 +120,6 @@
 /pc-bios/s390-ccw/s390-ccw.elf
 /pc-bios/s390-ccw/s390-ccw.img
 /docs/built
-/docs/interop/qemu-ga-qapi.texi
-/docs/interop/qemu-ga-ref.html
-/docs/interop/qemu-ga-ref.info*
-/docs/interop/qemu-ga-ref.txt
-/docs/interop/qemu-qmp-qapi.texi
-/docs/interop/qemu-qmp-ref.html
-/docs/interop/qemu-qmp-ref.info*
-/docs/interop/qemu-qmp-ref.txt
-/docs/version.texi
 /contrib/vhost-user-gpu/50-qemu-gpu.json
 *.tps
 .stgit-*
diff --git a/scripts/git.orderfile b/scripts/git.orderfile
index 73fd818d7f3..de276d6c8de 100644
--- a/scripts/git.orderfile
+++ b/scripts/git.orderfile
@@ -12,7 +12,6 @@
 # Documentation
 docs/*
 *.rst
-*.texi
 
 # build system
 configure
-- 
2.20.1



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

* [PATCH v5 19/20] configure: Drop texinfo requirement
  2020-08-10 19:49 [PATCH v5 00/20] Convert QAPI doc comments to generate rST instead of texinfo Peter Maydell
                   ` (17 preceding siblings ...)
  2020-08-10 19:50 ` [PATCH v5 18/20] Remove Texinfo related files from .gitignore and git.orderfile Peter Maydell
@ 2020-08-10 19:50 ` Peter Maydell
  2020-08-10 19:50 ` [PATCH v5 20/20] Remove texinfo dependency from docker and CI configs Peter Maydell
  2020-08-27 11:25 ` [PATCH v5 00/20] Convert QAPI doc comments to generate rST instead of texinfo Peter Maydell
  20 siblings, 0 replies; 62+ messages in thread
From: Peter Maydell @ 2020-08-10 19:50 UTC (permalink / raw)
  To: qemu-devel; +Cc: Markus Armbruster

We don't need the texinfo and pod2man programs to build our documentation
any more, so remove them from configure's tests.

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 configure | 12 ++----------
 1 file changed, 2 insertions(+), 10 deletions(-)

diff --git a/configure b/configure
index 2acc4d1465f..ae66ceeeaae 100755
--- a/configure
+++ b/configure
@@ -5105,14 +5105,14 @@ if test "$docs" != "no" ; then
   else
     sphinx_ok=no
   fi
-  if has makeinfo && has pod2man && test "$sphinx_ok" = "yes"; then
+  if test "$sphinx_ok" = "yes"; then
     docs=yes
   else
     if test "$docs" = "yes" ; then
       if has $sphinx_build && test "$sphinx_ok" != "yes"; then
         echo "Warning: $sphinx_build exists but it is either too old or uses too old a Python version" >&2
       fi
-      feature_not_found "docs" "Install texinfo, Perl/perl-podlators and a Python 3 version of python-sphinx"
+      feature_not_found "docs" "Install a Python 3 version of python-sphinx"
     fi
     docs=no
   fi
@@ -6593,13 +6593,6 @@ if test "$solaris" = "no" && test "$tsan" = "no"; then
     fi
 fi
 
-# test if pod2man has --utf8 option
-if pod2man --help | grep -q utf8; then
-    POD2MAN="pod2man --utf8"
-else
-    POD2MAN="pod2man"
-fi
-
 # Use ASLR, no-SEH and DEP if available
 if test "$mingw32" = "yes" ; then
     for flag in --dynamicbase --no-seh --nxcompat; do
@@ -8008,7 +8001,6 @@ echo "LDFLAGS_SHARED=$LDFLAGS_SHARED" >> $config_host_mak
 echo "LIBS_QGA+=$libs_qga" >> $config_host_mak
 echo "TASN1_LIBS=$tasn1_libs" >> $config_host_mak
 echo "TASN1_CFLAGS=$tasn1_cflags" >> $config_host_mak
-echo "POD2MAN=$POD2MAN" >> $config_host_mak
 if test "$gcov" = "yes" ; then
   echo "CONFIG_GCOV=y" >> $config_host_mak
   echo "GCOV=$gcov_tool" >> $config_host_mak
-- 
2.20.1



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

* [PATCH v5 20/20] Remove texinfo dependency from docker and CI configs
  2020-08-10 19:49 [PATCH v5 00/20] Convert QAPI doc comments to generate rST instead of texinfo Peter Maydell
                   ` (18 preceding siblings ...)
  2020-08-10 19:50 ` [PATCH v5 19/20] configure: Drop texinfo requirement Peter Maydell
@ 2020-08-10 19:50 ` Peter Maydell
  2020-08-27 11:25 ` [PATCH v5 00/20] Convert QAPI doc comments to generate rST instead of texinfo Peter Maydell
  20 siblings, 0 replies; 62+ messages in thread
From: Peter Maydell @ 2020-08-10 19:50 UTC (permalink / raw)
  To: qemu-devel; +Cc: Markus Armbruster

We don't need texinfo to build the docs any more, so we can
drop that dependency from our docker and other CI configs.

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
Changes v4->v5: remove texinfo from ubuntu2004.docker too
---
 .travis.yml                                | 1 -
 tests/docker/dockerfiles/debian10.docker   | 1 -
 tests/docker/dockerfiles/debian9.docker    | 1 +
 tests/docker/dockerfiles/fedora.docker     | 1 -
 tests/docker/dockerfiles/ubuntu.docker     | 1 -
 tests/docker/dockerfiles/ubuntu1804.docker | 1 -
 tests/docker/dockerfiles/ubuntu2004.docker | 1 -
 7 files changed, 1 insertion(+), 6 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 6695c0620fc..ff4361079f3 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -198,7 +198,6 @@ jobs:
         apt:
           packages:
             - python3-sphinx
-            - texinfo
             - perl
 
 
diff --git a/tests/docker/dockerfiles/debian10.docker b/tests/docker/dockerfiles/debian10.docker
index bcdff04ddfe..e5dbfa8fe95 100644
--- a/tests/docker/dockerfiles/debian10.docker
+++ b/tests/docker/dockerfiles/debian10.docker
@@ -30,7 +30,6 @@ RUN apt update && \
         psmisc \
         python3 \
         python3-sphinx \
-        texinfo \
         $(apt-get -s build-dep qemu | egrep ^Inst | fgrep '[all]' | cut -d\  -f2)
 
 ENV FEATURES docs
diff --git a/tests/docker/dockerfiles/debian9.docker b/tests/docker/dockerfiles/debian9.docker
index 0f0ebe530af..7e4b8a672fc 100644
--- a/tests/docker/dockerfiles/debian9.docker
+++ b/tests/docker/dockerfiles/debian9.docker
@@ -28,4 +28,5 @@ RUN apt update && \
         pkg-config \
         psmisc \
         python3 \
+        python3-sphinx \
         $(apt-get -s build-dep qemu | egrep ^Inst | fgrep '[all]' | cut -d\  -f2)
diff --git a/tests/docker/dockerfiles/fedora.docker b/tests/docker/dockerfiles/fedora.docker
index 70b6186bd3e..71e4b569770 100644
--- a/tests/docker/dockerfiles/fedora.docker
+++ b/tests/docker/dockerfiles/fedora.docker
@@ -96,7 +96,6 @@ ENV PACKAGES \
     tar \
     tesseract \
     tesseract-langpack-eng \
-    texinfo \
     usbredir-devel \
     virglrenderer-devel \
     vte291-devel \
diff --git a/tests/docker/dockerfiles/ubuntu.docker b/tests/docker/dockerfiles/ubuntu.docker
index 161806e6b8c..b556ed17d29 100644
--- a/tests/docker/dockerfiles/ubuntu.docker
+++ b/tests/docker/dockerfiles/ubuntu.docker
@@ -63,7 +63,6 @@ ENV PACKAGES \
     python3-yaml \
     python3-sphinx \
     sparse \
-    texinfo \
     xfslibs-dev
 RUN apt-get update && \
     DEBIAN_FRONTEND=noninteractive apt-get -y install $PACKAGES
diff --git a/tests/docker/dockerfiles/ubuntu1804.docker b/tests/docker/dockerfiles/ubuntu1804.docker
index a10ea2850b6..a6a7617da67 100644
--- a/tests/docker/dockerfiles/ubuntu1804.docker
+++ b/tests/docker/dockerfiles/ubuntu1804.docker
@@ -49,7 +49,6 @@ ENV PACKAGES \
     python3-yaml \
     python3-sphinx \
     sparse \
-    texinfo \
     xfslibs-dev
 RUN apt-get update && \
     DEBIAN_FRONTEND=noninteractive apt-get -y install $PACKAGES
diff --git a/tests/docker/dockerfiles/ubuntu2004.docker b/tests/docker/dockerfiles/ubuntu2004.docker
index 8d10934a2a7..cafe8443fbf 100644
--- a/tests/docker/dockerfiles/ubuntu2004.docker
+++ b/tests/docker/dockerfiles/ubuntu2004.docker
@@ -57,7 +57,6 @@ ENV PACKAGES flex bison \
     sparse \
     tesseract-ocr \
     tesseract-ocr-eng \
-    texinfo \
     xfslibs-dev\
     vim
 RUN apt-get update && \
-- 
2.20.1



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

* Re: [PATCH v5 02/20] qapi: Fix indentation, again
  2020-08-10 19:50 ` [PATCH v5 02/20] qapi: Fix indentation, again Peter Maydell
@ 2020-08-14 18:39   ` Richard Henderson
  0 siblings, 0 replies; 62+ messages in thread
From: Richard Henderson @ 2020-08-14 18:39 UTC (permalink / raw)
  To: Peter Maydell, qemu-devel; +Cc: Markus Armbruster

On 8/10/20 12:50 PM, Peter Maydell wrote:
> In commit 26ec4e53f2 and similar commits we fixed the indentation
> for doc comments in our qapi json files to follow a new stricter
> standard for indentation, which permits only:
>     @arg: description line 1
>           description line 2
> 
> or:
>     @arg:
>     line 1
>     line 2
> 
> Unfortunately since we didn't manage to get the script changes that
> enforced the new style in, a variety of commits (eg df4097aeaf71,
> 2e4457032105) introduced new doc text which doesn't follow the new
> stricter rules for indentation on multi-line doc comments.  Bring
> those into line with the new rules.
> 
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
> ---

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

r~


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

* Re: [PATCH v5 03/20] qapi/block-core.json: Fix nbd-server-start docs
  2020-08-10 19:50 ` [PATCH v5 03/20] qapi/block-core.json: Fix nbd-server-start docs Peter Maydell
@ 2020-08-14 18:39   ` Richard Henderson
  0 siblings, 0 replies; 62+ messages in thread
From: Richard Henderson @ 2020-08-14 18:39 UTC (permalink / raw)
  To: Peter Maydell, qemu-devel; +Cc: Markus Armbruster

On 8/10/20 12:50 PM, Peter Maydell wrote:
> Commit eed8b6917832 added some new text to the nbd-server-start
> documentation in the wrong place.  Since this is after the 'Returns:'
> line it's parsed as if it were part of the documentation of the
> "Returns:' information.  Move it up to join the rest of the
> "documentation of the type as a whole" doc text.
> 
> This doesn't look odd in the current HTML rendering, but the
> new QAPI-to-rST handling will complain about the indent level
> of the lines not matching up with the 'Returns:' line.
> 
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
> ---
>  qapi/block-core.json | 6 +++---
>  1 file changed, 3 insertions(+), 3 deletions(-)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

r~



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

* Re: [PATCH v5 09/20] docs/sphinx: Add new qapi-doc Sphinx extension
  2020-08-10 19:50 ` [PATCH v5 09/20] docs/sphinx: Add new qapi-doc Sphinx extension Peter Maydell
@ 2020-08-14 18:40   ` Richard Henderson
  2020-09-04 12:29   ` Markus Armbruster
  2020-09-04 14:44   ` Markus Armbruster
  2 siblings, 0 replies; 62+ messages in thread
From: Richard Henderson @ 2020-08-14 18:40 UTC (permalink / raw)
  To: Peter Maydell, qemu-devel; +Cc: Markus Armbruster

On 8/10/20 12:50 PM, Peter Maydell wrote:
> Some of our documentation is auto-generated from documentation
> comments in the JSON schema.
> 
> For Sphinx, rather than creating a file to include, the most natural
> way to handle this is to have a small custom Sphinx extension which
> processes the JSON file and inserts documentation into the rST
> file being processed.
> 
> This is the same approach that kerneldoc and hxtool use.
> 
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
> ---
> Changes v4->v5: match the changes in parameters to the
> various visit_* methods from commit 7b3bc9e28f366
> ---
>  docs/conf.py           |   6 +-
>  docs/sphinx/qapidoc.py | 504 +++++++++++++++++++++++++++++++++++++++++
>  MAINTAINERS            |   1 +
>  3 files changed, 510 insertions(+), 1 deletion(-)
>  create mode 100644 docs/sphinx/qapidoc.py

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

r~



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

* Re: [PATCH v5 00/20] Convert QAPI doc comments to generate rST instead of texinfo
  2020-08-10 19:49 [PATCH v5 00/20] Convert QAPI doc comments to generate rST instead of texinfo Peter Maydell
                   ` (19 preceding siblings ...)
  2020-08-10 19:50 ` [PATCH v5 20/20] Remove texinfo dependency from docker and CI configs Peter Maydell
@ 2020-08-27 11:25 ` Peter Maydell
  2020-09-04 14:34   ` Markus Armbruster
  20 siblings, 1 reply; 62+ messages in thread
From: Peter Maydell @ 2020-08-27 11:25 UTC (permalink / raw)
  To: QEMU Developers; +Cc: Markus Armbruster

On Mon, 10 Aug 2020 at 20:50, Peter Maydell <peter.maydell@linaro.org> wrote:
> This series switches all our QAPI doc comments over from
> texinfo format to rST. It then removes all the texinfo
> machinery, because this was the last user of texinfo.
>
> This is largely just a rebase of patchset v4 to current master.

> There are a few things I have left out of this initial series:

I realized there is something I forgot to add to this "left out" list:

Sphinx needs to know what all the input files which go into
a document are, as it builds up dependencies to tell it whether
to rebuild the output or not. The docs/sphinx/qapidoc.py
plugin adds such a dependency on the file that the .rst
docs reference (eg qapi/qapi-schema.json) but it does not
have a mechanism for adding dependencies when that .json
file uses an 'include' to pull in other .json files.

I'm not sure whether the scripts/qapi code supports telling
a consumer of the parsed info about this -- is it sufficient
for QAPISchemaGenRSTVisitor to implement the 'visit_include'
method, find the path to the included .qapi file from the
arguments and call Sphinx's env.notedependency(), or do we
need to do something more complicated to get the list of
all the included .qapi files ?

thanks
-- PMM


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

* Re: [PATCH v5 06/20] tests/qapi/doc-good.json: Prepare for qapi-doc Sphinx extension
  2020-08-10 19:50 ` [PATCH v5 06/20] tests/qapi/doc-good.json: Prepare for qapi-doc Sphinx extension Peter Maydell
@ 2020-09-04  8:10   ` Markus Armbruster
  2020-09-04 12:17     ` Peter Maydell
  0 siblings, 1 reply; 62+ messages in thread
From: Markus Armbruster @ 2020-09-04  8:10 UTC (permalink / raw)
  To: Peter Maydell; +Cc: qemu-devel

Peter Maydell <peter.maydell@linaro.org> writes:

> doc-good.json tests doc comment parser corner cases.  We're about to
> largely replace it by a Sphinx extension, which will have different
> corner cases.  Tweak the test so it passes both with the old parser
> and the Sphinx extension, by making it match the more restrictive
> rST syntax:
>
>  * in a single list the bullet types must all match
>  * lists must have leading and following blank lines
>  * indentation is important

Actually, indentation has always been important, but the conversion to
rST changes where and how it matters.  Perhaps:

   * the rules on when and where indentation matters differ

>  * the '|' example syntax is going to go away entirely, so stop
>    testing it
>
> This will avoid the tests spuriously breaking when we tighten up the
> parser code in the following commits.
>
> Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
> Reviewed-by: Markus Armbruster <armbru@redhat.com>
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>



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

* Re: [PATCH v5 08/20] scripts/qapi/parser.py: improve doc comment indent handling
  2020-08-10 19:50 ` [PATCH v5 08/20] scripts/qapi/parser.py: improve doc comment indent handling Peter Maydell
@ 2020-09-04  9:03   ` Markus Armbruster
  2020-09-21 15:06     ` Peter Maydell
  2020-09-22 15:28     ` Peter Maydell
  0 siblings, 2 replies; 62+ messages in thread
From: Markus Armbruster @ 2020-09-04  9:03 UTC (permalink / raw)
  To: Peter Maydell; +Cc: qemu-devel

Peter Maydell <peter.maydell@linaro.org> writes:

> Make the handling of indentation in doc comments more sophisticated,
> so that when we see a section like:
>
> Notes: some text
>        some more text
>           indented line 3
>
> we save it for the doc-comment processing code as:
>
> some text
> some more text
>    indented line 3
>
> and when we see a section with the heading on its own line:
>
> Notes:
>
> some text
> some more text
>    indented text
>
> we also accept that and save it in the same form.
>
> The exception is that we always retain indentation as-is for Examples
> sections, because these are literal text.

Does docs/devel/qapi-code-gen.txt need an update?  Hmm, looks like you
leave it to [PATCH 15] docs/devel/qapi-code-gen.txt: Update to new rST
backend conventions.  Acceptable.  Mentioning it in the commit message
now may make sense.

> If we detect that the comment document text is not indented as much
> as we expect it to be, we throw a parse error.  (We don't complain
> about over-indented sections, because for rST this can be legitimate
> markup.)
>
> The golden reference for the doc comment text is updated to remove
> the two 'wrong' indents; these now form a test case that we correctly
> stripped leading whitespace from an indented multi-line argument
> definition.
>
> Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
> ---
> v1->v2: Update doc-good.out as per final para.
> ---
>  scripts/qapi/parser.py         | 81 +++++++++++++++++++++++++++-------
>  tests/qapi-schema/doc-good.out |  4 +-
>  2 files changed, 67 insertions(+), 18 deletions(-)
>
> diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py
> index 7fae4478d34..d9f11eadd96 100644
> --- a/scripts/qapi/parser.py
> +++ b/scripts/qapi/parser.py
> @@ -308,18 +308,32 @@ class QAPIDoc:
>      """
>  
>      class Section:
> -        def __init__(self, name=None):
> +        def __init__(self, parser, name=None, indent=0):
> +            # parser, for error messages about indentation
> +            self._parser = parser
>              # optional section name (argument/member or section name)
>              self.name = name
>              # the list of lines for this section
>              self.text = ''
> +            # the expected indent level of the text of this section
> +            self._indent = indent
>  
>          def append(self, line):
> +            # Strip leading spaces corresponding to the expected indent level
> +            # Blank lines are always OK.
> +            if line:
> +                spacecount = len(line) - len(line.lstrip(" "))

Works, but I'd prefer

                   indent = re.match(r'\s*', line).end()

> +                if spacecount > self._indent:
> +                    spacecount = self._indent
> +                if spacecount < self._indent:
> +                    raise QAPIParseError(self._parser, "unexpected de-indent")

New error needs test coverage.  I append a possible test.

Reporting the expected indentation might be helpful.

> +                line = line[spacecount:]

If you use self._indent instead of spacecount here (which I find
clearer), you don't need to cap spacecount at self._indent above.

> +
>              self.text += line.rstrip() + '\n'
>  
>      class ArgSection(Section):
> -        def __init__(self, name):
> -            super().__init__(name)
> +        def __init__(self, parser, name, indent=0):
> +            super().__init__(parser, name, indent)
>              self.member = None
>  
>          def connect(self, member):
> @@ -333,7 +347,7 @@ class QAPIDoc:
>          self._parser = parser
>          self.info = info
>          self.symbol = None
> -        self.body = QAPIDoc.Section()
> +        self.body = QAPIDoc.Section(parser)
>          # dict mapping parameter name to ArgSection
>          self.args = OrderedDict()
>          self.features = OrderedDict()
> @@ -438,7 +452,18 @@ class QAPIDoc:
>  
>          if name.startswith('@') and name.endswith(':'):
>              line = line[len(name)+1:]
> -            self._start_args_section(name[1:-1])
> +            if not line or line.isspace():
> +                # Line was just the "@arg:" header; following lines
> +                # are not indented
> +                indent = 0
> +                line = ''
> +            else:
> +                # Line is "@arg: first line of description"; following
> +                # lines should be indented by len(name) + 1, and we
> +                # pad out this first line so it is handled the same way
> +                indent = len(name) + 1
> +                line = ' ' * indent + line
> +            self._start_args_section(name[1:-1], indent)
>          elif self._is_section_tag(name):
>              self._append_line = self._append_various_line
>              self._append_various_line(line)
> @@ -460,7 +485,17 @@ class QAPIDoc:
>  
>          if name.startswith('@') and name.endswith(':'):
>              line = line[len(name)+1:]
> -            self._start_features_section(name[1:-1])
> +            if not line or line.isspace():
> +                # Line is just the "@name:" header, no ident for following lines

pycodestyle complains:
scripts/qapi/parser.py:489:80: E501 line too long (80 > 79 characters)

> +                indent = 0
> +                line = ''
> +            else:
> +                # Line is "@arg: first line of description"; following
> +                # lines should be indented by len(name) + 3, and we
> +                # pad out this first line so it is handled the same way
> +                indent = len(name) + 1

Comment claims + 3, code uses + 1.

Does this do the right thing when @arg: is followed by multiple
whitespace characters?

> +                line = ' ' * indent + line
> +            self._start_features_section(name[1:-1], indent)
>          elif self._is_section_tag(name):
>              self._append_line = self._append_various_line
>              self._append_various_line(line)
> @@ -493,11 +528,23 @@ class QAPIDoc:
>                                   % (name, self.sections[0].name))
>          if self._is_section_tag(name):
>              line = line[len(name)+1:]
> -            self._start_section(name[:-1])
> +            if not line or line.isspace():
> +                # Line is just "SectionName:", no indent for following lines
> +                indent = 0
> +                line = ''
> +            elif name.startswith("Example"):
> +                # The "Examples" section is literal-text, so preserve
> +                # all the indentation as-is
> +                indent = 0

Section "Example" is an exception.  Needs to be documented.  Do we
really need the exception?  As far as I can see, it's only ever used in
documentation of block-latency-histogram-set.

> +            else:
> +                # Line is "SectionName: some text", indent required

Same situation as above, much terser comment.

> +                indent = len(name) + 1
> +                line = ' ' * indent + line
> +            self._start_section(name[:-1], indent)
>  
>          self._append_freeform(line)
>  
> -    def _start_symbol_section(self, symbols_dict, name):
> +    def _start_symbol_section(self, symbols_dict, name, indent):
>          # FIXME invalid names other than the empty string aren't flagged
>          if not name:
>              raise QAPIParseError(self._parser, "invalid parameter name")
> @@ -506,21 +553,21 @@ class QAPIDoc:
>                                   "'%s' parameter name duplicated" % name)
>          assert not self.sections
>          self._end_section()
> -        self._section = QAPIDoc.ArgSection(name)
> +        self._section = QAPIDoc.ArgSection(self._parser, name, indent)
>          symbols_dict[name] = self._section
>  
> -    def _start_args_section(self, name):
> -        self._start_symbol_section(self.args, name)
> +    def _start_args_section(self, name, indent):
> +        self._start_symbol_section(self.args, name, indent)
>  
> -    def _start_features_section(self, name):
> -        self._start_symbol_section(self.features, name)
> +    def _start_features_section(self, name, indent):
> +        self._start_symbol_section(self.features, name, indent)
>  
> -    def _start_section(self, name=None):
> +    def _start_section(self, name=None, indent=0):
>          if name in ('Returns', 'Since') and self.has_section(name):
>              raise QAPIParseError(self._parser,
>                                   "duplicated '%s' section" % name)
>          self._end_section()
> -        self._section = QAPIDoc.Section(name)
> +        self._section = QAPIDoc.Section(self._parser, name, indent)
>          self.sections.append(self._section)
>  
>      def _end_section(self):
> @@ -543,7 +590,7 @@ class QAPIDoc:
>      def connect_member(self, member):
>          if member.name not in self.args:
>              # Undocumented TODO outlaw
> -            self.args[member.name] = QAPIDoc.ArgSection(member.name)
> +            self.args[member.name] = QAPIDoc.ArgSection(self._parser, member.name)

pycodestyle complains:
scripts/qapi/parser.py:593:80: E501 line too long (82 > 79 characters)


>          self.args[member.name].connect(member)
>  
>      def connect_feature(self, feature):
> @@ -551,6 +598,8 @@ class QAPIDoc:
>              raise QAPISemError(feature.info,
>                                 "feature '%s' lacks documentation"
>                                 % feature.name)
> +            self.features[feature.name] = QAPIDoc.ArgSection(self._parser,
> +                                                             feature.name)

pylint points out:
scripts/qapi/parser.py:601:12: W0101: Unreachable code (unreachable)

>          self.features[feature.name].connect(feature)
>  
>      def check_expr(self, expr):
> diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out
> index 0ef85d959ac..bbf77b08dc3 100644
> --- a/tests/qapi-schema/doc-good.out
> +++ b/tests/qapi-schema/doc-good.out
> @@ -158,7 +158,7 @@ doc symbol=Alternate
>  
>      arg=i
>  an integer
> -    @b is undocumented
> +@b is undocumented
>      arg=b
>  
>      feature=alt-feat
> @@ -173,7 +173,7 @@ doc symbol=cmd
>  the first argument
>      arg=arg2
>  the second
> -       argument
> +argument
>      arg=arg3
>  
>      feature=cmd-feat1


Suggested new test doc-bad-deintent.json, cribbed from your PATCH 06 of
doc-good.json:

##
# @Alternate:
# @i: an integer
# @b is undocumented
##
{ 'alternate': 'Alternate',
  'data': { 'i': 'int', 'b': 'bool' } }



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

* Re: [PATCH v5 06/20] tests/qapi/doc-good.json: Prepare for qapi-doc Sphinx extension
  2020-09-04  8:10   ` Markus Armbruster
@ 2020-09-04 12:17     ` Peter Maydell
  0 siblings, 0 replies; 62+ messages in thread
From: Peter Maydell @ 2020-09-04 12:17 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: QEMU Developers

On Fri, 4 Sep 2020 at 09:10, Markus Armbruster <armbru@redhat.com> wrote:
>
> Peter Maydell <peter.maydell@linaro.org> writes:
>
> > doc-good.json tests doc comment parser corner cases.  We're about to
> > largely replace it by a Sphinx extension, which will have different
> > corner cases.  Tweak the test so it passes both with the old parser
> > and the Sphinx extension, by making it match the more restrictive
> > rST syntax:
> >
> >  * in a single list the bullet types must all match
> >  * lists must have leading and following blank lines
> >  * indentation is important
>
> Actually, indentation has always been important, but the conversion to
> rST changes where and how it matters.  Perhaps:
>
>    * the rules on when and where indentation matters differ

The commit message text here is the text you recommended in your
review of v4 of this series...

https://lists.gnu.org/archive/html/qemu-devel/2020-08/msg00988.html

thanks
-- PMM


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

* Re: [PATCH v5 09/20] docs/sphinx: Add new qapi-doc Sphinx extension
  2020-08-10 19:50 ` [PATCH v5 09/20] docs/sphinx: Add new qapi-doc Sphinx extension Peter Maydell
  2020-08-14 18:40   ` Richard Henderson
@ 2020-09-04 12:29   ` Markus Armbruster
  2020-09-21 18:06     ` Peter Maydell
  2020-09-04 14:44   ` Markus Armbruster
  2 siblings, 1 reply; 62+ messages in thread
From: Markus Armbruster @ 2020-09-04 12:29 UTC (permalink / raw)
  To: Peter Maydell; +Cc: qemu-devel

Peter Maydell <peter.maydell@linaro.org> writes:

> Some of our documentation is auto-generated from documentation
> comments in the JSON schema.
>
> For Sphinx, rather than creating a file to include, the most natural
> way to handle this is to have a small custom Sphinx extension which
> processes the JSON file and inserts documentation into the rST
> file being processed.
>
> This is the same approach that kerneldoc and hxtool use.
>
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

Got a pointer to "Writing Sphinx Extensions for Dummies" or similar?

Since I know nothing about Sphinx extensions, my review will be more or
less limited to the part facing QAPI.  Perhaps "generated docs look
sane" suffices for the rest.

> ---
> Changes v4->v5: match the changes in parameters to the
> various visit_* methods from commit 7b3bc9e28f366
> ---
>  docs/conf.py           |   6 +-
>  docs/sphinx/qapidoc.py | 504 +++++++++++++++++++++++++++++++++++++++++
>  MAINTAINERS            |   1 +
>  3 files changed, 510 insertions(+), 1 deletion(-)
>  create mode 100644 docs/sphinx/qapidoc.py
>
> diff --git a/docs/conf.py b/docs/conf.py
> index d6e173ef77b..44ec5050717 100644
> --- a/docs/conf.py
> +++ b/docs/conf.py
> @@ -52,7 +52,10 @@ except NameError:
>  # add these directories to sys.path here. If the directory is relative to the
>  # documentation root, use an absolute path starting from qemu_docdir.
>  #
> +# Our extensions are in docs/sphinx; the qapidoc extension requires
> +# the QAPI modules from scripts/.
>  sys.path.insert(0, os.path.join(qemu_docdir, "sphinx"))
> +sys.path.insert(0, os.path.join(qemu_docdir, "../scripts"))
>  
>  
>  # -- General configuration ------------------------------------------------
> @@ -67,7 +70,7 @@ needs_sphinx = '1.6'
>  # Add any Sphinx extension module names here, as strings. They can be
>  # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
>  # ones.
> -extensions = ['kerneldoc', 'qmp_lexer', 'hxtool']
> +extensions = ['kerneldoc', 'qmp_lexer', 'hxtool', 'qapidoc']
>  
>  # Add any paths that contain templates here, relative to this directory.
>  templates_path = ['_templates']
> @@ -241,3 +244,4 @@ texinfo_documents = [
>  kerneldoc_bin = os.path.join(qemu_docdir, '../scripts/kernel-doc')
>  kerneldoc_srctree = os.path.join(qemu_docdir, '..')
>  hxtool_srctree = os.path.join(qemu_docdir, '..')
> +qapidoc_srctree = os.path.join(qemu_docdir, '..')
> diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py
> new file mode 100644
> index 00000000000..4f571b2f45a
> --- /dev/null
> +++ b/docs/sphinx/qapidoc.py
> @@ -0,0 +1,504 @@
> +# coding=utf-8
> +#
> +# QEMU qapidoc QAPI file parsing extension
> +#
> +# Copyright (c) 2020 Linaro
> +#
> +# This work is licensed under the terms of the GNU GPLv2 or later.
> +# See the COPYING file in the top-level directory.
> +"""qapidoc is a Sphinx extension that implements the qapi-doc directive"""
> +
> +# The purpose of this extension is to read the documentation comments
> +# in QAPI JSON schema files, and insert them all into the current document.

Let's drop "JSON", it's not really true.

> +# The conf.py file must set the qapidoc_srctree config value to
> +# the root of the QEMU source tree.
> +# Each qapi-doc:: directive takes one argument which is the
> +# path of the .json file to process, relative to the source tree.

Beg your pardon?  Oh, you're talking about .rst files now.  The next two
patches will add such files.  Perhaps something like "The qapi-doc::
reST directive provided by this extension takes ..."

> +
> +import os
> +import re
> +
> +from docutils import nodes
> +from docutils.statemachine import ViewList
> +from docutils.parsers.rst import directives, Directive
> +from sphinx.errors import ExtensionError
> +from sphinx.util.nodes import nested_parse_with_titles
> +import sphinx
> +from qapi.gen import QAPISchemaVisitor
> +from qapi.schema import QAPIError, QAPISchema
> +
> +# Sphinx up to 1.6 uses AutodocReporter; 1.7 and later
> +# use switch_source_input. Check borrowed from kerneldoc.py.
> +Use_SSI = sphinx.__version__[:3] >= '1.7'
> +if Use_SSI:
> +    from sphinx.util.docutils import switch_source_input
> +else:
> +    from sphinx.ext.autodoc import AutodocReporter
> +
> +
> +__version__ = '1.0'
> +
> +# Function borrowed from pydash, which is under the MIT license
> +def intersperse(iterable, separator):
> +    """Like join, but for arbitrary iterables, notably arrays"""
> +    iterable = iter(iterable)
> +    yield next(iterable)
> +    for item in iterable:
> +        yield separator
> +        yield item

What's wrong with separator.join(iterable)?

> +
> +class QAPISchemaGenRSTVisitor(QAPISchemaVisitor):
> +    """A QAPI schema visitor which generates docutils/Sphinx nodes
> +
> +    This class builds up a tree of docutils/Sphinx nodes corresponding
> +    to documentation for the various QAPI objects. To use it, first create
> +    a QAPISchemaGenRSTVisitor object, and call its visit_begin() method.
> +    Then you can call one of the two methods 'freeform' (to add documentation
> +    for a freeform documentation chunk) or 'symbol' (to add documentation
> +    for a QAPI symbol). These will cause the visitor to build up the
> +    tree of document nodes. Once you've added all the documentation
> +    via 'freeform' and 'symbol' method calls, you can call 'get_document_nodes'
> +    to get the final list of document nodes (in a form suitable for returning
> +    from a Sphinx directive's 'run' method).
> +    """
> +    def __init__(self, sphinx_directive):
> +        self._cur_doc = None
> +        self._sphinx_directive = sphinx_directive
> +        self._top_node = nodes.section()
> +        self._active_headings = [self._top_node]
> +
> +    def _serror(self, msg):
> +        """Raise an exception giving a user-friendly syntax error message"""
> +        file = self._cur_doc.info.fname
> +        line = self._cur_doc.info.line
> +        raise ExtensionError('%s line %d: syntax error: %s' % (file, line, msg))

Note for later: ornate error message format, no use of QAPISourceInfo.

> +
> +    def _make_dlitem(self, term, defn):
> +        """Return a dlitem node with the specified term and definition.
> +
> +        term should be a list of Text and literal nodes.
> +        defn should be one of:
> +        - a string, which will be handed to _parse_text_into_node
> +        - a list of Text and literal nodes, which will be put into
> +          a paragraph node
> +        """
> +        dlitem = nodes.definition_list_item()
> +        dlterm = nodes.term('', '', *term)
> +        dlitem += dlterm
> +        if defn:
> +            dldef = nodes.definition()
> +            if isinstance(defn, list):
> +                dldef += nodes.paragraph('', '', *defn)
> +            else:
> +                self._parse_text_into_node(defn, dldef)
> +            dlitem += dldef
> +        return dlitem
> +
> +    def _make_section(self, title):
> +        """Return a section node with optional title"""
> +        section = nodes.section(ids=[self._sphinx_directive.new_serialno()])
> +        if title:
> +            section += nodes.title(title, title)
> +        return section
> +
> +    def _nodes_for_ifcond(self, ifcond, with_if=True):
> +        """Return list of Text, literal nodes for the ifcond
> +
> +        Return a list which gives text like ' (If: cond1, cond2, cond3)', where
> +        the conditions are in literal-text and the commas are not.
> +        If with_if is False, we don't return the "(If: " and ")".
> +        """
> +        condlist = intersperse([nodes.literal('', c) for c in ifcond],
> +                               nodes.Text(', '))
> +        if not with_if:
> +            return condlist
> +
> +        nodelist = [nodes.Text(' ('), nodes.strong('', 'If: ')]
> +        nodelist.extend(condlist)
> +        nodelist.append(nodes.Text(')'))
> +        return nodelist
> +
> +    def _nodes_for_one_member(self, member):
> +        """Return list of Text, literal nodes for this member
> +
> +        Return a list of doctree nodes which give text like
> +        'name: type (optional) (If: ...)' suitable for use as the
> +        'term' part of a definition list item.
> +        """
> +        term = [nodes.literal('', member.name)]
> +        if member.type.doc_type():
> +            term.append(nodes.Text(': '))
> +            term.append(nodes.literal('', member.type.doc_type()))
> +        if member.optional:
> +            term.append(nodes.Text(' (optional)'))
> +        if member.ifcond:
> +            term.extend(self._nodes_for_ifcond(member.ifcond))
> +        return term
> +
> +    def _nodes_for_variant_when(self, variants, variant):
> +        """Return list of Text, literal nodes for variant 'when' clause
> +
> +        Return a list of doctree nodes which give text like
> +        'when tagname is variant (If: ...)' suitable for use in
> +        the 'variants' part of a definition list.
> +        """
> +        term = [nodes.Text(' when '),
> +                nodes.literal('', variants.tag_member.name),
> +                nodes.Text(' is '),
> +                nodes.literal('', '"%s"' % variant.name)]
> +        if variant.ifcond:
> +            term.extend(self._nodes_for_ifcond(variant.ifcond))
> +        return term
> +
> +    def _nodes_for_members(self, doc, what, base=None, variants=None):
> +        """Return doctree nodes for the table of members"""
> +        dlnode = nodes.definition_list()
> +        for section in doc.args.values():
> +            term = self._nodes_for_one_member(section.member)
> +            # TODO drop fallbacks when undocumented members are outlawed
> +            if section.text:
> +                defn = section.text
> +            elif (variants and variants.tag_member == section.member
> +                  and not section.member.type.doc_type()):
> +                values = section.member.type.member_names()
> +                defn = [nodes.Text('One of ')]
> +                defn.extend(intersperse([nodes.literal('', v) for v in values],
> +                                        nodes.Text(', ')))
> +            else:
> +                defn = [nodes.Text('Not documented')]
> +
> +            dlnode += self._make_dlitem(term, defn)
> +
> +        if base:
> +            dlnode += self._make_dlitem([nodes.Text('The members of '),
> +                                         nodes.literal('', base.doc_type())],
> +                                        None)
> +
> +        if variants:
> +            for v in variants.variants:
> +                if v.type.is_implicit():
> +                    assert not v.type.base and not v.type.variants
> +                    for m in v.type.local_members:
> +                        term = self._nodes_for_one_member(m)
> +                        term.extend(self._nodes_for_variant_when(variants, v))
> +                        dlnode += self._make_dlitem(term, None)
> +                else:
> +                    term = [nodes.Text('The members of '),
> +                            nodes.literal('', v.type.doc_type())]
> +                    term.extend(self._nodes_for_variant_when(variants, v))
> +                    dlnode += self._make_dlitem(term, None)
> +
> +        if not dlnode.children:
> +            return None
> +
> +        section = self._make_section(what)
> +        section += dlnode
> +        return section
> +
> +    def _nodes_for_enum_values(self, doc, what):

@what is only ever 'Values', isn't it?

> +        """Return doctree nodes for the table of enum values"""
> +        seen_item = False
> +        dlnode = nodes.definition_list()
> +        for section in doc.args.values():
> +            termtext = [nodes.literal('', section.member.name)]
> +            if section.member.ifcond:
> +                termtext.extend(self._nodes_for_ifcond(section.member.ifcond))
> +            # TODO drop fallbacks when undocumented members are outlawed
> +            if section.text:
> +                defn = section.text
> +            else:
> +                defn = [nodes.Text('Not documented')]
> +
> +            dlnode += self._make_dlitem(termtext, defn)
> +            seen_item = True
> +
> +        if not seen_item:
> +            return None
> +
> +        section = self._make_section(what)
> +        section += dlnode
> +        return section
> +
> +    def _nodes_for_arguments(self, doc, boxed_arg_type):
> +        """Return doctree nodes for the arguments section"""
> +        if boxed_arg_type:
> +            assert not doc.args
> +            section = self._make_section('Arguments')
> +            dlnode = nodes.definition_list()
> +            dlnode += self._make_dlitem(
> +                [nodes.Text('The members of '),
> +                 nodes.literal('', boxed_arg_type.name)],
> +                None)
> +            section += dlnode
> +            return section
> +
> +        return self._nodes_for_members(doc, 'Arguments')
> +
> +    def _nodes_for_features(self, doc):
> +        """Return doctree nodes for the table of features"""
> +        seen_item = False
> +        dlnode = nodes.definition_list()
> +        for section in doc.features.values():
> +            dlnode += self._make_dlitem([nodes.literal('', section.name)],
> +                                        section.text)
> +            seen_item = True
> +
> +        if not seen_item:
> +            return None
> +
> +        section = self._make_section('Features')
> +        section += dlnode
> +        return section
> +
> +    def _nodes_for_example(self, exampletext):
> +        """Return doctree nodes for a code example snippet"""
> +        return nodes.literal_block(exampletext, exampletext)
> +
> +    def _nodes_for_sections(self, doc, ifcond):
> +        """Return doctree nodes for additional sections following arguments"""
> +        nodelist = []
> +        for section in doc.sections:
> +            snode = self._make_section(section.name)
> +            if section.name and section.name.startswith('Example'):
> +                snode += self._nodes_for_example(section.text)
> +            else:
> +                self._parse_text_into_node(section.text, snode)
> +            nodelist.append(snode)
> +        if ifcond:
> +            snode = self._make_section('If')
> +            snode += self._nodes_for_ifcond(ifcond, with_if=False)
> +            nodelist.append(snode)

I think I'd rather have a separate _node_for_ifcond().  Not a demand.

> +        if not nodelist:
> +            return None

Any particular reason for mapping [] to None?

> +        return nodelist
> +
> +    def _add_doc(self, typ, sections):
> +        """Add documentation for a command/object/enum...
> +
> +        We assume we're documenting the thing defined in self._cur_doc.
> +        typ is the type of thing being added ("Command", "Object", etc)
> +
> +        sections is a list of nodes for sections to add to the definition.
> +        """
> +
> +        doc = self._cur_doc
> +        snode = nodes.section(ids=[self._sphinx_directive.new_serialno()])
> +        snode += nodes.title('', '', *[nodes.literal(doc.symbol, doc.symbol),
> +                                       nodes.Text(' (' + typ + ')')])
> +        self._parse_text_into_node(doc.body.text, snode)
> +        for s in sections:
> +            if s is not None:
> +                snode += s

Looks like you're flattening the two-level list the callers create,
e.g. ...

> +        self._add_node_to_current_heading(snode)
> +
> +    def visit_enum_type(self, name, info, ifcond, features, members, prefix):
> +        doc = self._cur_doc
> +        self._add_doc('Enum',
> +                      [self._nodes_for_enum_values(doc, 'Values'),
> +                       self._nodes_for_features(doc),
> +                       self._nodes_for_sections(doc, ifcond)])

... this one: [[nodes for enum values...]
               [nodes for features...]
               [nodes for sections]]

Makes me wonder why you build two levels in the first place.

> +
> +    def visit_object_type(self, name, info, ifcond, features,
> +                          base, members, variants):
> +        doc = self._cur_doc
> +        if base and base.is_implicit():
> +            base = None
> +        self._add_doc('Object',
> +                      [self._nodes_for_members(doc, 'Members', base, variants),
> +                       self._nodes_for_features(doc),
> +                       self._nodes_for_sections(doc, ifcond)])
> +
> +    def visit_alternate_type(self, name, info, ifcond, features, variants):
> +        doc = self._cur_doc
> +        self._add_doc('Alternate',
> +                      [self._nodes_for_members(doc, 'Members'),
> +                       self._nodes_for_features(doc),
> +                       self._nodes_for_sections(doc, ifcond)])
> +
> +    def visit_command(self, name, info, ifcond, features, arg_type,
> +                      ret_type, gen, success_response, boxed, allow_oob,
> +                      allow_preconfig):
> +        doc = self._cur_doc
> +        self._add_doc('Command',
> +                      [self._nodes_for_arguments(doc,
> +                                                 arg_type if boxed else None),
> +                       self._nodes_for_features(doc),
> +                       self._nodes_for_sections(doc, ifcond)])
> +
> +    def visit_event(self, name, info, ifcond, features, arg_type, boxed):
> +        doc = self._cur_doc
> +        self._add_doc('Event',
> +                      [self._nodes_for_arguments(doc,
> +                                                 arg_type if boxed else None),
> +                       self._nodes_for_features(doc),
> +                       self._nodes_for_sections(doc, ifcond)])
> +
> +    def symbol(self, doc, entity):
> +        """Add documentation for one symbol to the document tree
> +
> +        This is the main entry point which causes us to add documentation
> +        nodes for a symbol (which could be a 'command', 'object', 'event',
> +        etc). We do this by calling 'visit' on the schema entity, which
> +        will then call back into one of our visit_* methods, depending
> +        on what kind of thing this symbol is.
> +        """
> +        self._cur_doc = doc
> +        entity.visit(self)
> +        self._cur_doc = None

Matches doc.py's QAPISchemaGenDocVisitor.symbol().  entity.visit() calls
one of the five functions preceding this one.  The function called
processes self._cur_doc.  They all boil down to a variation of
self._add_doc().  Okay.

> +
> +    def _start_new_heading(self, heading, level):
> +        """Start a new heading at the specified heading level
> +
> +        Create a new section whose title is 'heading' and which is placed
> +        in the docutils node tree as a child of the most recent level-1
> +        heading. Subsequent document sections (commands, freeform doc chunks,
> +        etc) will be placed as children of this new heading section.
> +        """
> +        if len(self._active_headings) < level:
> +            self._serror('Level %d subheading found outside a level %d heading'
> +                         % (level, level - 1))
> +        snode = self._make_section(heading)
> +        self._active_headings[level - 1] += snode
> +        self._active_headings = self._active_headings[:level]
> +        self._active_headings.append(snode)
> +
> +    def _add_node_to_current_heading(self, node):
> +        """Add the node to whatever the current active heading is"""
> +        self._active_headings[-1] += node
> +
> +    def freeform(self, doc):
> +        """Add a piece of 'freeform' documentation to the document tree
> +
> +        A 'freeform' document chunk doesn't relate to any particular
> +        symbol (for instance, it could be an introduction).
> +
> +        As a special case, if the freeform document is a single line
> +        of the form '= Heading text' it is treated as a section or subsection
> +        heading, with the heading level indicated by the number of '=' signs.
> +        """
> +
> +        # QAPIDoc documentation says free-form documentation blocks
> +        # must have only a body section, nothing else.
> +        assert not doc.sections
> +        assert not doc.args
> +        assert not doc.features
> +        self._cur_doc = doc
> +
> +        if re.match(r'=+ ', doc.body.text):
> +            # Section or subsection heading: must be the only thing in the block
> +            (heading, _, rest) = doc.body.text.partition('\n')
> +            if rest != '':
> +                raise ExtensionError('%s line %s: section or subsection heading'
> +                                     ' must be in its own doc comment block'
> +                                     % (doc.info.fname, doc.info.line))

Same ornate error message format as above, less 'syntax error: '.

> +            (leader, _, heading) = heading.partition(' ')
> +            self._start_new_heading(heading, len(leader))
> +            return
> +
> +        node = self._make_section(None)
> +        self._parse_text_into_node(doc.body.text, node)
> +        self._add_node_to_current_heading(node)
> +        self._cur_doc = None
> +
> +    def _parse_text_into_node(self, doctext, node):
> +        """Parse a chunk of QAPI-doc-format text into the node
> +
> +        The doc comment can contain most inline rST markup, including
> +        bulleted and enumerated lists.
> +        As an extra permitted piece of markup, @var will be turned
> +        into ``var``.
> +        """
> +
> +        # Handle the "@var means ``var`` case
> +        doctext = re.sub(r'@([\w-]+)', r'``\1``', doctext)
> +
> +        rstlist = ViewList()
> +        for line in doctext.splitlines():
> +            # The reported line number will always be that of the start line
> +            # of the doc comment, rather than the actual location of the error.
> +            # Being more precise would require overhaul of the QAPIDoc class
> +            # to track lines more exactly within all the sub-parts of the doc
> +            # comment, as well as counting lines here.
> +            rstlist.append(line, self._cur_doc.info.fname,
> +                           self._cur_doc.info.line)
> +        self._sphinx_directive.do_parse(rstlist, node)
> +
> +    def get_document_nodes(self):
> +        """Return the list of docutils nodes which make up the document"""
> +        return self._top_node.children
> +
> +class QAPIDocDirective(Directive):
> +    """Extract documentation from the specified QAPI .json file"""
> +    required_argument = 1
> +    optional_arguments = 1
> +    option_spec = {
> +        'qapifile': directives.unchanged_required
> +    }
> +    has_content = False
> +
> +    def new_serialno(self):
> +        """Return a unique new ID string suitable for use as a node's ID"""
> +        env = self.state.document.settings.env
> +        return 'qapidoc-%d' % env.new_serialno('qapidoc')
> +
> +    def run(self):
> +        env = self.state.document.settings.env
> +        qapifile = env.config.qapidoc_srctree + '/' + self.arguments[0]
> +
> +        # Tell sphinx of the dependency
> +        env.note_dependency(os.path.abspath(qapifile))
> +
> +        try:
> +            schema = QAPISchema(qapifile)
> +        except QAPIError as err:
> +            # Launder QAPI parse errors into Sphinx extension errors
> +            # so they are displayed nicely to the user
> +            raise ExtensionError(str(err))

I expected plumbing the error location through Sphinx to the user to
take a bit more effort.  I'm not complaining.

A QAPIError looks like this when converted to str:

    In file included from ...:
    ...
    In file included from ...:
    FILE: In ENTITY-TYPE 'NAME':
    FILE:LINE: MESSAGE

"In file" lines appear when the error is in a sub-module.

An "In ENTITY-TYPE" line appears when the error is in an entity
definition.

The other two ExtensionError() look like

    FILE line LINE: syntax error: MESSAGE

and

    FILE line LINE: MESSAGE

More ornate, less information.

I figure more errors can get thrown by nested_parse_with_titles() (see
below).  How do they look like?

Can we avoid the information loss?

Can we make the error message format more consistent?

> +
> +        vis = QAPISchemaGenRSTVisitor(self)
> +        vis.visit_begin(schema)
> +        for doc in schema.docs:
> +            if doc.symbol:
> +                vis.symbol(doc, schema.lookup_entity(doc.symbol))
> +            else:
> +                vis.freeform(doc)

Matches doc.py.  Visits the documentation doc comment blocks and the
entities they document.  The other backends use

           schema.visit(vis)

which visits the modules and their entities.  Okay.

> +
> +        return vis.get_document_nodes()
> +
> +    def do_parse(self, rstlist, node):
> +        """Parse rST source lines and add them to the specified node
> +
> +        Take the list of rST source lines rstlist, parse them as
> +        rST, and add the resulting docutils nodes as children of node.
> +        The nodes are parsed in a way that allows them to include
> +        subheadings (titles) without confusing the rendering of
> +        anything else.
> +        """
> +        # This is from kerneldoc.py -- it works around an API change in
> +        # Sphinx between 1.6 and 1.7. Unlike kerneldoc.py, we use
> +        # sphinx.util.nodes.nested_parse_with_titles() rather than the
> +        # plain self.state.nested_parse(), and so we can drop the saving
> +        # of title_styles and section_level that kerneldoc.py does,
> +        # because nested_parse_with_titles() does that for us.
> +        if Use_SSI:
> +            with switch_source_input(self.state, rstlist):
> +                nested_parse_with_titles(self.state, rstlist, node)
> +        else:
> +            save = self.state.memo.reporter
> +            self.state.memo.reporter = AutodocReporter(rstlist,
> +                                                       self.state.memo.reporter)
> +            try:
> +                nested_parse_with_titles(self.state, rstlist, node)
> +            finally:
> +                self.state.memo.reporter = save
> +
> +def setup(app):
> +    """ Register qapi-doc directive with Sphinx"""
> +    app.add_config_value('qapidoc_srctree', None, 'env')
> +    app.add_directive('qapi-doc', QAPIDocDirective)
> +
> +    return dict(
> +        version=__version__,
> +        parallel_read_safe=True,
> +        parallel_write_safe=True
> +    )
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 0886eb3d2b5..fdd38f412d7 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -3116,6 +3116,7 @@ M: Peter Maydell <peter.maydell@linaro.org>
>  S: Maintained
>  F: docs/conf.py
>  F: docs/*/conf.py
> +F: docs/sphinx/
>  
>  Miscellaneous
>  -------------

I had to read mostly backwards to understand the code.



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

* Re: [PATCH v5 12/20] qapi: Use rST markup for literal blocks
  2020-08-10 19:50 ` [PATCH v5 12/20] qapi: Use rST markup for literal blocks Peter Maydell
@ 2020-09-04 13:02   ` Markus Armbruster
  0 siblings, 0 replies; 62+ messages in thread
From: Markus Armbruster @ 2020-09-04 13:02 UTC (permalink / raw)
  To: Peter Maydell; +Cc: qemu-devel

Peter Maydell <peter.maydell@linaro.org> writes:

> There are exactly two places in our json doc comments where we
> use the markup accepted by the texi doc generator where a '|' in
> the first line of a doc comment means the line should be emitted
> as a literal block (fixed-width font, whitespace preserved).

Has always been pretty broken, though: each line ends up in its own
@example environment.  See doc-good.texi.

> Since we use this syntax so rarely, instead of making the rST
> generator support it, instead just convert the two uses to
> rST-format literal blocks, which are indented and introduced
> with '::'.

Also, we should not reinvent what reST already provides.

> (The rST generator doesn't complain about the old style syntax,
> it just emits it with the '|' and with the whitespace not
> preserved, which looks odd, but means we can safely leave this
> change until after we've stopped generating texinfo.)

In other words, the recent switch to the reST generator messed these
examples up, and this commit tidies up.

> Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
> ---
>  qapi/block-core.json  | 16 +++++++++-------
>  qapi/qapi-schema.json |  6 ++++--
>  2 files changed, 13 insertions(+), 9 deletions(-)
>
> diff --git a/qapi/block-core.json b/qapi/block-core.json
> index 535b2b2e7bf..12758116e85 100644
> --- a/qapi/block-core.json
> +++ b/qapi/block-core.json
> @@ -566,13 +566,15 @@
>  #        For the example above, @bins may be something like [3, 1, 5, 2],
>  #        and corresponding histogram looks like:
>  #
> -# |      5|           *
> -# |      4|           *
> -# |      3| *         *
> -# |      2| *         *    *
> -# |      1| *    *    *    *
> -# |       +------------------
> -# |           10   50   100
> +# ::
> +#
> +#        5|           *
> +#        4|           *
> +#        3| *         *
> +#        2| *         *    *
> +#        1| *    *    *    *
> +#         +------------------
> +#             10   50   100
>  #
>  # Since: 4.0
>  ##

Could exploit that reST is the Perl of ASCII-based markups, and write

   #        For the example above, @bins may be something like [3, 1, 5, 2],
   #        and corresponding histogram looks like::
   #
   #        5|           *
   #        4|           *
   #        3| *         *
   #        2| *         *    *
   #        1| *    *    *    *
   #         +------------------
   #             10   50   100
   #
   # Since: 4.0
   ##

Matter of taste.  I find both similarly arcane.

> diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json
> index 5fc0771eb04..c19b4267058 100644
> --- a/qapi/qapi-schema.json
> +++ b/qapi/qapi-schema.json
> @@ -23,8 +23,10 @@
>  #
>  # Example:
>  #
> -# | -> data issued by the Client
> -# | <- Server data response
> +# ::
> +#
> +#   -> data issued by the Client
> +#   <- Server data response
>  #
>  # Please, refer to the QMP specification (docs/interop/qmp-spec.txt) for
>  # detailed information on the Server command and response formats.

Likewise.

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



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

* Re: [PATCH v5 10/20] docs/interop: Convert qemu-ga-ref to rST
  2020-08-10 19:50 ` [PATCH v5 10/20] docs/interop: Convert qemu-ga-ref to rST Peter Maydell
@ 2020-09-04 13:16   ` Markus Armbruster
  2020-09-04 13:18     ` Peter Maydell
  2020-09-21 15:30     ` Peter Maydell
  0 siblings, 2 replies; 62+ messages in thread
From: Markus Armbruster @ 2020-09-04 13:16 UTC (permalink / raw)
  To: Peter Maydell; +Cc: qemu-devel

Peter Maydell <peter.maydell@linaro.org> writes:

> Convert qemu-ga-ref to rST format. This includes dropping
> the plain-text, pdf and info format outputs for this document;
> as with all our other Sphinx-based documentation, we provide
> HTML and manpage only.
>
> The qemu-ga-ref.rst is somewhat more stripped down than
> the .texi was, because we do not (currently) attempt to
> generate indexes for the commands, events and data types
> being documented.

I'll miss the plain text output (I may get by with formatting the man
output, but even if that works, it's still an extra step).  Others may
miss the index.  Oh well, march of progress.

> As the GA ref is now part of the Sphinx 'interop' manual,
> we can delete the direct link from index.html.in.
>
> Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
[...]
> diff --git a/docs/interop/qemu-ga-ref.rst b/docs/interop/qemu-ga-ref.rst
> new file mode 100644
> index 00000000000..013eac0bb53
> --- /dev/null
> +++ b/docs/interop/qemu-ga-ref.rst
> @@ -0,0 +1,4 @@
> +QEMU Guest Agent Protocol Reference
> +===================================
> +
> +.. qapi-doc:: qga/qapi-schema.json
> diff --git a/docs/interop/qemu-ga-ref.texi b/docs/interop/qemu-ga-ref.texi
> deleted file mode 100644
> index ddb76ce1c2a..00000000000
> --- a/docs/interop/qemu-ga-ref.texi
> +++ /dev/null
> @@ -1,80 +0,0 @@
> -\input texinfo
> -@setfilename qemu-ga-ref.info
> -
> -@include version.texi
> -
> -@exampleindent 0
> -@paragraphindent 0
> -
> -@settitle QEMU Guest Agent Protocol Reference
> -
> -@iftex
> -@center @image{docs/qemu_logo}
> -@end iftex
> -
> -@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

Does the interop manual carry an equivalent copyright notice?

[...]
> diff --git a/Makefile b/Makefile
> index 13dd708c4af..f0cca10b427 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -371,7 +371,7 @@ DOCS+=$(MANUAL_BUILDDIR)/tools/virtiofsd.1
>  endif
>  DOCS+=$(MANUAL_BUILDDIR)/system/qemu-block-drivers.7
>  DOCS+=docs/interop/qemu-qmp-ref.html docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7
> -DOCS+=docs/interop/qemu-ga-ref.html docs/interop/qemu-ga-ref.txt docs/interop/qemu-ga-ref.7
> +DOCS+=$(MANUAL_BUILDDIR)/interop/qemu-ga-ref.7
>  DOCS+=$(MANUAL_BUILDDIR)/system/qemu-cpu-models.7
>  DOCS+=$(MANUAL_BUILDDIR)/index.html
>  ifdef CONFIG_VIRTFS
> @@ -800,11 +800,11 @@ distclean: clean
>  	rm -f config.log
>  	rm -f linux-headers/asm
>  	rm -f docs/version.texi
> -	rm -f docs/interop/qemu-ga-qapi.texi docs/interop/qemu-qmp-qapi.texi
> -	rm -f docs/interop/qemu-qmp-ref.7 docs/interop/qemu-ga-ref.7
> -	rm -f docs/interop/qemu-qmp-ref.txt docs/interop/qemu-ga-ref.txt
> -	rm -f docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf
> -	rm -f docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html
> +	rm -f docs/interop/qemu-qmp-qapi.texi
> +	rm -f docs/interop/qemu-qmp-ref.7
> +	rm -f docs/interop/qemu-qmp-ref.txt
> +	rm -f docs/interop/qemu-qmp-ref.pdf
> +	rm -f docs/interop/qemu-qmp-ref.html
>  	rm -rf .doctrees
>  	$(call clean-manual,devel)
>  	$(call clean-manual,interop)

Please update .gitignore. as well.

[...]

Comments apply to the next patch, too.



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

* Re: [PATCH v5 10/20] docs/interop: Convert qemu-ga-ref to rST
  2020-09-04 13:16   ` Markus Armbruster
@ 2020-09-04 13:18     ` Peter Maydell
  2020-09-21 15:30     ` Peter Maydell
  1 sibling, 0 replies; 62+ messages in thread
From: Peter Maydell @ 2020-09-04 13:18 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: QEMU Developers

On Fri, 4 Sep 2020 at 14:16, Markus Armbruster <armbru@redhat.com> wrote:
>
> Peter Maydell <peter.maydell@linaro.org> writes:
>
> > Convert qemu-ga-ref to rST format. This includes dropping
> > the plain-text, pdf and info format outputs for this document;
> > as with all our other Sphinx-based documentation, we provide
> > HTML and manpage only.
> >
> > The qemu-ga-ref.rst is somewhat more stripped down than
> > the .texi was, because we do not (currently) attempt to
> > generate indexes for the commands, events and data types
> > being documented.
>
> I'll miss the plain text output (I may get by with formatting the man
> output, but even if that works, it's still an extra step).  Others may
> miss the index.  Oh well, march of progress.

The index is definitely something I'd like to restore at some
later date (for all the manuals, not just this one).

thanks
-- PMM


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

* Re: [PATCH v5 14/20] scripts/qapi: Remove texinfo generation support
  2020-08-10 19:50 ` [PATCH v5 14/20] scripts/qapi: Remove texinfo generation support Peter Maydell
@ 2020-09-04 13:37   ` Markus Armbruster
  2020-09-24 18:14     ` Peter Maydell
  0 siblings, 1 reply; 62+ messages in thread
From: Markus Armbruster @ 2020-09-04 13:37 UTC (permalink / raw)
  To: Peter Maydell; +Cc: qemu-devel, Markus Armbruster

Peter Maydell <peter.maydell@linaro.org> writes:

> We no longer use the generated texinfo format documentation,
> so delete the code that generates it, and the test case for
> the generation.
>
> Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
> ---
>  Makefile                        |   1 -
>  scripts/qapi-gen.py             |   2 -
>  scripts/qapi/doc.py             | 302 ------------------------------
>  scripts/qapi/gen.py             |   7 -
>  tests/Makefile.include          |  15 +-
>  tests/qapi-schema/doc-good.texi | 313 --------------------------------
>  6 files changed, 1 insertion(+), 639 deletions(-)
>  delete mode 100644 scripts/qapi/doc.py
>  delete mode 100644 tests/qapi-schema/doc-good.texi
>
> diff --git a/Makefile b/Makefile
> index 3df1cf68333..fc3ccc15030 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -626,7 +626,6 @@ qemu-keymap$(EXESUF): QEMU_CFLAGS += $(XKBCOMMON_CFLAGS)
>  qapi-py = $(SRC_PATH)/scripts/qapi/__init__.py \
>  $(SRC_PATH)/scripts/qapi/commands.py \
>  $(SRC_PATH)/scripts/qapi/common.py \
> -$(SRC_PATH)/scripts/qapi/doc.py \
>  $(SRC_PATH)/scripts/qapi/error.py \
>  $(SRC_PATH)/scripts/qapi/events.py \
>  $(SRC_PATH)/scripts/qapi/expr.py \
> diff --git a/scripts/qapi-gen.py b/scripts/qapi-gen.py
> index 4b03f7d53be..541e8c1f55d 100755
> --- a/scripts/qapi-gen.py
> +++ b/scripts/qapi-gen.py
> @@ -10,7 +10,6 @@ import re
>  import sys
>  
>  from qapi.commands import gen_commands
> -from qapi.doc import gen_doc
>  from qapi.events import gen_events
>  from qapi.introspect import gen_introspect
>  from qapi.schema import QAPIError, QAPISchema
> @@ -51,7 +50,6 @@ def main(argv):
>      gen_commands(schema, args.output_dir, args.prefix)
>      gen_events(schema, args.output_dir, args.prefix)
>      gen_introspect(schema, args.output_dir, args.prefix, args.unmask)
> -    gen_doc(schema, args.output_dir, args.prefix)
>  
>  
>  if __name__ == '__main__':
> diff --git a/scripts/qapi/doc.py b/scripts/qapi/doc.py
> deleted file mode 100644
> index 7764de1e4bc..00000000000
> --- a/scripts/qapi/doc.py
> +++ /dev/null
[...]
> diff --git a/scripts/qapi/gen.py b/scripts/qapi/gen.py
> index bf5552a4e7f..ca66c82b5b8 100644
> --- a/scripts/qapi/gen.py
> +++ b/scripts/qapi/gen.py
> @@ -178,13 +178,6 @@ def ifcontext(ifcond, *args):
>          arg.end_if()
>  
>  
> -class QAPIGenDoc(QAPIGen):
> -
> -    def _top(self):
> -        return (super()._top()
> -                + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n')
> -
> -
>  class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor):
>  
>      def __init__(self, prefix, what, blurb, pydoc):
> diff --git a/tests/Makefile.include b/tests/Makefile.include
> index c7e4646ded7..ec83efeaa63 100644
> --- a/tests/Makefile.include
> +++ b/tests/Makefile.include
> @@ -38,7 +38,6 @@ export SRC_PATH
>  qapi-py = $(SRC_PATH)/scripts/qapi/__init__.py \
>  $(SRC_PATH)/scripts/qapi/commands.py \
>  $(SRC_PATH)/scripts/qapi/common.py \
> -$(SRC_PATH)/scripts/qapi/doc.py \
>  $(SRC_PATH)/scripts/qapi/error.py \
>  $(SRC_PATH)/scripts/qapi/events.py \
>  $(SRC_PATH)/scripts/qapi/expr.py \
> @@ -501,16 +500,8 @@ tests/test-qapi-gen-timestamp: \
>  	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \
>  		-o tests -p "test-" $<, \
>  		"GEN","$(@:%-timestamp=%)")
> -	@rm -f tests/test-qapi-doc.texi
>  	@>$@
>  
> -tests/qapi-schema/doc-good.test.texi: $(SRC_PATH)/tests/qapi-schema/doc-good.json $(qapi-py)
> -	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \
> -		-o tests/qapi-schema -p "doc-good-" $<, \
> -		"GEN","$@")
> -	@mv tests/qapi-schema/doc-good-qapi-doc.texi $@
> -	@rm -f tests/qapi-schema/doc-good-qapi-*.[ch] tests/qapi-schema/doc-good-qmp-*.[ch]
> -
>  tests/qtest/dbus-vmstate1.h tests/qtest/dbus-vmstate1.c: tests/qtest/dbus-vmstate1-gen-timestamp ;
>  tests/qtest/dbus-vmstate1-gen-timestamp: $(SRC_PATH)/tests/qtest/dbus-vmstate1.xml
>  	$(call quiet-command,$(GDBUS_CODEGEN) $< \
> @@ -891,10 +882,6 @@ check-tests/qapi-schema/frontend: $(addprefix $(SRC_PATH)/, $(check-qapi-schema-
>  	  PYTHONIOENCODING=utf-8 $(PYTHON) $(SRC_PATH)/tests/qapi-schema/test-qapi.py $^, \
>  	  TEST, check-qapi-schema)
>  
> -.PHONY: check-tests/qapi-schema/doc-good.texi
> -check-tests/qapi-schema/doc-good.texi: tests/qapi-schema/doc-good.test.texi
> -	@diff -u $(SRC_PATH)/tests/qapi-schema/doc-good.texi $<
> -

We shouldn't just delete this test.

It is for checking the doc generator does what it should for "good"
input.  "Bad" input is coverd by the other doc-*.json.

With the old doc generation system, the testing "good" input is
straightforward: generate Texinfo, diff to expected Texinfo, which is
committed to git.

This test has been invaliable when maintaining and extending doc.py.

With the new system, there is no ouput suitable for diffing, as the
various outputs all depend on the version of Sphinx.

Or is there?  Is there a way to have Sphinx "unparse" its internal
representation of the input?

If not, we should still run doc-good.json through the doc generator to
at least catch errors.  We still lose the ability to catch silent bad
output.

>  .PHONY: check-decodetree
>  check-decodetree:
>  	$(call quiet-command, \
> @@ -956,7 +943,7 @@ check-acceptance: check-venv $(TESTS_RESULTS_DIR) get-vm-images
>  # Consolidated targets
>  
>  .PHONY: check-block check-qapi-schema check-qtest check-unit check check-clean get-vm-images
> -check-qapi-schema: check-tests/qapi-schema/frontend check-tests/qapi-schema/doc-good.texi
> +check-qapi-schema: check-tests/qapi-schema/frontend
>  check-qtest: $(patsubst %,check-qtest-%, $(QTEST_TARGETS))
>  ifeq ($(CONFIG_TOOLS),y)
>  check-block: $(patsubst %,check-%, $(check-block-y))
> diff --git a/tests/qapi-schema/doc-good.texi b/tests/qapi-schema/doc-good.texi
> deleted file mode 100644
> index 12808989ffb..00000000000
> --- a/tests/qapi-schema/doc-good.texi
> +++ /dev/null
[...]



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

* Re: [PATCH v5 00/20] Convert QAPI doc comments to generate rST instead of texinfo
  2020-08-27 11:25 ` [PATCH v5 00/20] Convert QAPI doc comments to generate rST instead of texinfo Peter Maydell
@ 2020-09-04 14:34   ` Markus Armbruster
  2020-09-04 14:48     ` Peter Maydell
  0 siblings, 1 reply; 62+ messages in thread
From: Markus Armbruster @ 2020-09-04 14:34 UTC (permalink / raw)
  To: Peter Maydell; +Cc: QEMU Developers

Peter Maydell <peter.maydell@linaro.org> writes:

> On Mon, 10 Aug 2020 at 20:50, Peter Maydell <peter.maydell@linaro.org> wrote:
>> This series switches all our QAPI doc comments over from
>> texinfo format to rST. It then removes all the texinfo
>> machinery, because this was the last user of texinfo.
>>
>> This is largely just a rebase of patchset v4 to current master.

It needs another rebase now, for the Meson build system.

>> There are a few things I have left out of this initial series:
>
> I realized there is something I forgot to add to this "left out" list:
>
> Sphinx needs to know what all the input files which go into
> a document are, as it builds up dependencies to tell it whether
> to rebuild the output or not. The docs/sphinx/qapidoc.py
> plugin adds such a dependency on the file that the .rst
> docs reference (eg qapi/qapi-schema.json)

In QAPIDocDirective.run():

        # Tell sphinx of the dependency
        env.note_dependency(os.path.abspath(qapifile))


>                                           but it does not
> have a mechanism for adding dependencies when that .json
> file uses an 'include' to pull in other .json files.
>
> I'm not sure whether the scripts/qapi code supports telling
> a consumer of the parsed info about this -- is it sufficient
> for QAPISchemaGenRSTVisitor to implement the 'visit_include'
> method, find the path to the included .qapi file from the
> arguments and call Sphinx's env.notedependency(), or do we
> need to do something more complicated to get the list of
> all the included .qapi files ?

Visitors can implement visit_include() to see include directives.
QAPISchemaModularCVisitor does, to generate #include that mirror the
source schema.  This is not what your want.

You want visit_module().  The appended hack makes qapi-gen.py spit out
the modules when it generates types, e.g.:

    $ python3 -B scripts/qapi-gen.py -o scratch tests/qapi-schema/qapi-schema-test.json 
    ### None
    ### 'qapi-schema-test.json'
    ### 'include/sub-module.json'
    ### 'sub-sub-module.json'

As you can see, the module names are file names relative to the main
module's directory.

Module None is for built-ins.

Unfortunately, your qapidoc.py bypasses the regular schema.visit() just
like old doc.py does, and for the same reason: it wants to iterate over
doc comments, not definitions.  Doc comments were bolted on later, and
it still shows.

I figure the clean solution is making schema.visit() pass doc comments
just like it passes entities.  Gets rid of the need to bypass
schema.visit().  Requires surgery to QAPISchema and QAPISchemaParser.

A quick hack: use a trivial QAPISchemaVisitor just to collect the
modules.

Questions?



diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py
index 3ad33af4ee..cec89e199c 100644
--- a/scripts/qapi/types.py
+++ b/scripts/qapi/types.py
@@ -274,6 +274,10 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor):
         # gen_object() is recursive, ensure it doesn't visit the empty type
         objects_seen.add(schema.the_empty_object_type.name)
 
+    def visit_module(self, name):
+        print('### %r' % name)
+        super().visit_module(name)
+
     def _gen_type_cleanup(self, name):
         self._genh.add(gen_type_cleanup_decl(name))
         self._genc.add(gen_type_cleanup(name))



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

* Re: [PATCH v5 09/20] docs/sphinx: Add new qapi-doc Sphinx extension
  2020-08-10 19:50 ` [PATCH v5 09/20] docs/sphinx: Add new qapi-doc Sphinx extension Peter Maydell
  2020-08-14 18:40   ` Richard Henderson
  2020-09-04 12:29   ` Markus Armbruster
@ 2020-09-04 14:44   ` Markus Armbruster
  2020-09-04 14:52     ` Peter Maydell
  2020-09-21 16:50     ` Peter Maydell
  2 siblings, 2 replies; 62+ messages in thread
From: Markus Armbruster @ 2020-09-04 14:44 UTC (permalink / raw)
  To: Peter Maydell; +Cc: qemu-devel

$ pycodestyle docs/sphinx/qapidoc.py 
docs/sphinx/qapidoc.py:42:1: E302 expected 2 blank lines, found 1
docs/sphinx/qapidoc.py:50:1: E302 expected 2 blank lines, found 1
docs/sphinx/qapidoc.py:74:80: E501 line too long (80 > 79 characters)
docs/sphinx/qapidoc.py:388:80: E501 line too long (80 > 79 characters)
docs/sphinx/qapidoc.py:391:80: E501 line too long (80 > 79 characters)
docs/sphinx/qapidoc.py:430:1: E302 expected 2 blank lines, found 1
docs/sphinx/qapidoc.py:489:80: E501 line too long (80 > 79 characters)
docs/sphinx/qapidoc.py:495:1: E302 expected 2 blank lines, found 1
$ PYTHONPATH=scripts pylint docs/sphinx/qapidoc.py 
************* Module qapidoc
docs/sphinx/qapidoc.py:36:4: E0611: No name 'AutodocReporter' in module 'sphinx.ext.autodoc' (no-name-in-module)
docs/sphinx/qapidoc.py:45:10: R1708: Do not raise StopIteration in generator, use return statement instead (stop-iteration-return)
docs/sphinx/qapidoc.py:104:4: R0201: Method could be a function (no-self-use)
docs/sphinx/qapidoc.py:253:4: R0201: Method could be a function (no-self-use)
docs/sphinx/qapidoc.py:34:4: C0412: Imports from package sphinx are not grouped (ungrouped-imports)

------------------------------------------------------------------
Your code has been rated at 9.64/10 (previous run: 8.85/10, +0.79)

Use your judgement.



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

* Re: [PATCH v5 00/20] Convert QAPI doc comments to generate rST instead of texinfo
  2020-09-04 14:34   ` Markus Armbruster
@ 2020-09-04 14:48     ` Peter Maydell
  2020-09-04 15:54       ` Markus Armbruster
  0 siblings, 1 reply; 62+ messages in thread
From: Peter Maydell @ 2020-09-04 14:48 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: QEMU Developers

On Fri, 4 Sep 2020 at 15:34, Markus Armbruster <armbru@redhat.com> wrote:
> Peter Maydell <peter.maydell@linaro.org> writes:
> > I'm not sure whether the scripts/qapi code supports telling
> > a consumer of the parsed info about this -- is it sufficient
> > for QAPISchemaGenRSTVisitor to implement the 'visit_include'
> > method, find the path to the included .qapi file from the
> > arguments and call Sphinx's env.notedependency(), or do we
> > need to do something more complicated to get the list of
> > all the included .qapi files ?
>
> Visitors can implement visit_include() to see include directives.
> QAPISchemaModularCVisitor does, to generate #include that mirror the
> source schema.  This is not what your want.

Why not? Surely "see include directives" is exactly what I want?
Any time the QAPI parser opens a file that's different from the
initial one we want to know about it.

> You want visit_module().  The appended hack makes qapi-gen.py spit out
> the modules when it generates types, e.g.:
>
>     $ python3 -B scripts/qapi-gen.py -o scratch tests/qapi-schema/qapi-schema-test.json
>     ### None
>     ### 'qapi-schema-test.json'
>     ### 'include/sub-module.json'
>     ### 'sub-sub-module.json'

What's a "module" here ? Does this still produce output if the
top level .json file includes a sub-json file that doesn't actually
have any contents ? (We still want to generate the dependency
then, so we update the docs if the included file is edited to
add content.)

thanks
-- PMM


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

* Re: [PATCH v5 09/20] docs/sphinx: Add new qapi-doc Sphinx extension
  2020-09-04 14:44   ` Markus Armbruster
@ 2020-09-04 14:52     ` Peter Maydell
  2020-09-21 16:50     ` Peter Maydell
  1 sibling, 0 replies; 62+ messages in thread
From: Peter Maydell @ 2020-09-04 14:52 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: QEMU Developers

On Fri, 4 Sep 2020 at 15:44, Markus Armbruster <armbru@redhat.com> wrote:
>
> $ pycodestyle docs/sphinx/qapidoc.py
> docs/sphinx/qapidoc.py:42:1: E302 expected 2 blank lines, found 1
> docs/sphinx/qapidoc.py:50:1: E302 expected 2 blank lines, found 1
> docs/sphinx/qapidoc.py:74:80: E501 line too long (80 > 79 characters)
> docs/sphinx/qapidoc.py:388:80: E501 line too long (80 > 79 characters)
> docs/sphinx/qapidoc.py:391:80: E501 line too long (80 > 79 characters)
> docs/sphinx/qapidoc.py:430:1: E302 expected 2 blank lines, found 1
> docs/sphinx/qapidoc.py:489:80: E501 line too long (80 > 79 characters)
> docs/sphinx/qapidoc.py:495:1: E302 expected 2 blank lines, found 1

Those seem like easy fixes.

> $ PYTHONPATH=scripts pylint docs/sphinx/qapidoc.py
> ************* Module qapidoc
> docs/sphinx/qapidoc.py:36:4: E0611: No name 'AutodocReporter' in module 'sphinx.ext.autodoc' (no-name-in-module)
> docs/sphinx/qapidoc.py:45:10: R1708: Do not raise StopIteration in generator, use return statement instead (stop-iteration-return)
> docs/sphinx/qapidoc.py:104:4: R0201: Method could be a function (no-self-use)
> docs/sphinx/qapidoc.py:253:4: R0201: Method could be a function (no-self-use)
> docs/sphinx/qapidoc.py:34:4: C0412: Imports from package sphinx are not grouped (ungrouped-imports)
>
> ------------------------------------------------------------------
> Your code has been rated at 9.64/10 (previous run: 8.85/10, +0.79)
>
> Use your judgement.

I'm pretty sure I remember running this or something with very
similar output at some point. IIRC they're mostly largely
unavoidable (in particular 1 and 5 are not satisfiable because
we're trying to handle multiple versions of Sphinx which present
different sets of importable symbols.)

thanks
-- PMM


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

* Re: [PATCH v5 00/20] Convert QAPI doc comments to generate rST instead of texinfo
  2020-09-04 14:48     ` Peter Maydell
@ 2020-09-04 15:54       ` Markus Armbruster
  2020-09-04 16:05         ` Peter Maydell
  0 siblings, 1 reply; 62+ messages in thread
From: Markus Armbruster @ 2020-09-04 15:54 UTC (permalink / raw)
  To: Peter Maydell; +Cc: QEMU Developers

Peter Maydell <peter.maydell@linaro.org> writes:

> On Fri, 4 Sep 2020 at 15:34, Markus Armbruster <armbru@redhat.com> wrote:
>> Peter Maydell <peter.maydell@linaro.org> writes:
>> > I'm not sure whether the scripts/qapi code supports telling
>> > a consumer of the parsed info about this -- is it sufficient
>> > for QAPISchemaGenRSTVisitor to implement the 'visit_include'
>> > method, find the path to the included .qapi file from the
>> > arguments and call Sphinx's env.notedependency(), or do we
>> > need to do something more complicated to get the list of
>> > all the included .qapi files ?
>>
>> Visitors can implement visit_include() to see include directives.
>> QAPISchemaModularCVisitor does, to generate #include that mirror the
>> source schema.  This is not what your want.
>
> Why not? Surely "see include directives" is exactly what I want?
> Any time the QAPI parser opens a file that's different from the
> initial one we want to know about it.
>
>> You want visit_module().  The appended hack makes qapi-gen.py spit out
>> the modules when it generates types, e.g.:
>>
>>     $ python3 -B scripts/qapi-gen.py -o scratch tests/qapi-schema/qapi-schema-test.json
>>     ### None
>>     ### 'qapi-schema-test.json'
>>     ### 'include/sub-module.json'
>>     ### 'sub-sub-module.json'
>
> What's a "module" here ?

Initially, the include directive was just that: include another file's
contents right here.

Back in 2018, we switched from generating monolithic code to generating
modular code.  What does that mean?

Instead of generating the kitchen sink into a single qapi-types.c, we
split out the stuff generated for each FOO.json included by
qapi-schema.json into qapi-types-FOO.c.

Same for qapi-types.h, but with #include stitching that mirrors the
schema's include directives.  So, if FOO.json includes SUB.json, then
qapi-types-FOO.h will include qapi-types-SUB.h.

Same for qapi-{commands,events,visit}.{c,h}.

The qapi-schema.json (rather: the file you pass to qapi-gen.py) is the
main module.

The .json it includes are the sub-modules.

visit_module() lets you see the modules.

visit_include() lets you see the includes.  The same module can be
included multiple times.  Having to filter that out would be annoying.

>                          Does this still produce output if the
> top level .json file includes a sub-json file that doesn't actually
> have any contents ? (We still want to generate the dependency
> then, so we update the docs if the included file is edited to
> add content.)

If it doesn't, I'd be willing to call it a bug.  I dimly remember fixing
this (or a similar bug) before.



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

* Re: [PATCH v5 00/20] Convert QAPI doc comments to generate rST instead of texinfo
  2020-09-04 15:54       ` Markus Armbruster
@ 2020-09-04 16:05         ` Peter Maydell
  2020-09-24 14:13           ` Peter Maydell
  0 siblings, 1 reply; 62+ messages in thread
From: Peter Maydell @ 2020-09-04 16:05 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: QEMU Developers

On Fri, 4 Sep 2020 at 16:54, Markus Armbruster <armbru@redhat.com> wrote:
> Initially, the include directive was just that: include another file's
> contents right here.
>
> Back in 2018, we switched from generating monolithic code to generating
> modular code.  What does that mean?
>
> Instead of generating the kitchen sink into a single qapi-types.c, we
> split out the stuff generated for each FOO.json included by
> qapi-schema.json into qapi-types-FOO.c.
>
> Same for qapi-types.h, but with #include stitching that mirrors the
> schema's include directives.  So, if FOO.json includes SUB.json, then
> qapi-types-FOO.h will include qapi-types-SUB.h.
>
> Same for qapi-{commands,events,visit}.{c,h}.
>
> The qapi-schema.json (rather: the file you pass to qapi-gen.py) is the
> main module.
>
> The .json it includes are the sub-modules.
>
> visit_module() lets you see the modules.
>
> visit_include() lets you see the includes.  The same module can be
> included multiple times.  Having to filter that out would be annoying.

I don't think you'd need to filter it out for this use case -- I
assume Sphinx would just ignore the unnecessary extra dependency
information. But if visit_module() does what we want and you
recommend it I'll look at that.

thanks
-- PMM


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

* Re: [PATCH v5 15/20] docs/devel/qapi-code-gen.txt: Update to new rST backend conventions
  2020-08-10 19:50 ` [PATCH v5 15/20] docs/devel/qapi-code-gen.txt: Update to new rST backend conventions Peter Maydell
@ 2020-09-17  9:24   ` Markus Armbruster
  0 siblings, 0 replies; 62+ messages in thread
From: Markus Armbruster @ 2020-09-17  9:24 UTC (permalink / raw)
  To: Peter Maydell; +Cc: qemu-devel

Peter Maydell <peter.maydell@linaro.org> writes:

> Update the documentation of QAPI document comment syntax to match
> the new rST backend requirements. The principal changes are:
>  * whitespace is now significant,

Well, differently significant :)

>                                   and multiline definitions
>    must have their second and subsequent lines indented to
>    match the first line
>  * general rST format markup is permitted, not just the small
>    set of markup the old texinfo generator handled. For most
>    things (notably bulleted and itemized lists) the old format
>    is the same as rST was.
>  * Specific things that might trip people up:
>    - instead of *bold* and _italic_ rST has **bold** and *italic*
>    - lists need a preceding and following blank line
>    - a lone literal '*' will need to be backslash-escaped to
>      avoid a rST syntax error
>  * the old leading '|' for example (literal text) blocks is
>    replaced by the standard rST '::' literal block.
>  * headings and subheadings must now be in a freeform
>    documentation comment of their own
>  * we support arbitrary levels of sub- and sub-sub-heading, not
>    just a main and sub-heading like the old texinfo generator

Possibly noteworthy: lists can now be nested.

>
> Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
> ---
>  docs/devel/qapi-code-gen.txt | 90 ++++++++++++++++++++++++------------
>  1 file changed, 61 insertions(+), 29 deletions(-)
>
> diff --git a/docs/devel/qapi-code-gen.txt b/docs/devel/qapi-code-gen.txt
> index 69eede6c283..263f1c0b44c 100644
> --- a/docs/devel/qapi-code-gen.txt
> +++ b/docs/devel/qapi-code-gen.txt
> @@ -824,21 +824,39 @@ See below for more on definition documentation.
>  Free-form documentation may be used to provide additional text and
>  structuring content.
>  
> +==== Headings and subheadings ====
> +
> +A free-form documentation comment containing a single line
> +which starts with some '=' symbols and then a space defines
> +a section heading:
> +
> +    ##
> +    # = This is a top level heading
> +    ##
> +
> +    ##
> +    # This is a free-form comment which will go under the
> +    # top level heading.
> +    ##
> +
> +    ##
> +    # == This is a second level heading
> +    ##
> +
> +Section headings must always be correctly nested, so you can only
> +define a third-level heading inside a second-level heading, and so
> +on. The documentation generator will catch nesting mistakes and report
> +a syntax error.

Is the last sentence is useful?  We don't explicitly document other
parse errors.

>  
>  ==== Documentation markup ====
>  
> -Comment text starting with '=' is a section title:
> +Documentation comments can use most rST markup. In particular,

Please put two spaces after sentence-ending punctuation, for local
consistency, and to keep Emacs sentence commands working.

> +a '::' literal block can be used for examples:
>  
> -    # = Section title
> -
> -Double the '=' for a subsection title:
> -
> -    # == Subsection title
> -
> -'|' denotes examples:
> -
> -    # | Text of the example, may span
> -    # | multiple lines
> +    # ::
> +    #
> +    #   Text of the example, may span
> +    #   multiple lines
>  
>  '*' starts an itemized list:
>  
> @@ -854,37 +872,35 @@ A decimal number followed by '.' starts a numbered list:
>      #    multiple lines
>      # 2. Second item
>  
> -The actual number doesn't matter.  You could even use '*' instead of
> -'2.' for the second item.
> +The actual number doesn't matter.
>  
> -Lists can't be nested.  Blank lines are currently not supported within
> -lists.
> +Lists of either kind must be preceded and followed by a blank line.
> +If a list item's text spans multiple lines, then the second and
> +subsequent lines must be correctly indented to line up with the
> +first character of the first line.
>  
> -Additional whitespace between the initial '#' and the comment text is
> -permitted.
> -
> -*foo* and _foo_ are for strong and emphasis styles respectively (they
> -do not work over multiple lines).  @foo is used to reference a name in
> -the schema.
> +The usual '**strong**', '*emphasised*' and '``literal``' markup should
> +be used. If you need a single literal '*' you will need to backslash-escape it.

Long line.

> +As an extension beyond the usual rST syntax, you can also
> +use '@foo' to reference a name in the schema; this is rendered
> +the same way as '``foo``'.
>  
>  Example:
>  
>  ##
> -# = Section
> -# == Subsection
> -#
> -# Some text foo with *strong* and _emphasis_
> +# Some text foo with **bol** and *emphasis*

Do you mean **bold**?

>  # 1. with a list
>  # 2. like that
>  #
>  # And some code:
> -# | $ echo foo
> -# | -> do this
> -# | <- get that
>  #
> +# ::
> +#
> +#   $ echo foo
> +#   -> do this
> +#   <- get that
>  ##
>  
> -

Did you mean to drop the blank line?

>  ==== Definition documentation ====
>  
>  Definition documentation, if present, must immediately precede the
> @@ -899,6 +915,12 @@ commands and events), member (for structs and unions), branch (for
>  alternates), or value (for enums), and finally optional tagged
>  sections.
>  
> +Descriptions of arguments can span multiple lines; if they
> +do then the second and subsequent lines must be indented
> +to line up with the first character of the first line of the
> +description. The parser will report a syntax error if there
> +is insufficient indentation.

Is the last sentence is useful?

> +
>  FIXME: the parser accepts these things in almost any order.
>  FIXME: union branches should be described, too.
>  
> @@ -912,6 +934,16 @@ The section ends with the start of a new section.
>  A 'Since: x.y.z' tagged section lists the release that introduced the
>  definition.
>  
> +The text of a section can start on a new line, in
> +which case it must not be indented at all. It can also start
> +on the same line as the 'Note:', 'Returns:', etc tag. In this
> +case if it spans multiple lines then second and subsequent
> +lines must be indented to match the first.
> +
> +An 'Example' or 'Examples' section is automatically rendered
> +entirely as literal fixed-width text. In other sections,
> +the text is formatted, and rST markup can be used.
> +
>  For example:
>  
>  ##



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

* Re: [PATCH v5 08/20] scripts/qapi/parser.py: improve doc comment indent handling
  2020-09-04  9:03   ` Markus Armbruster
@ 2020-09-21 15:06     ` Peter Maydell
  2020-09-22  7:27       ` Markus Armbruster
  2020-09-22 15:28     ` Peter Maydell
  1 sibling, 1 reply; 62+ messages in thread
From: Peter Maydell @ 2020-09-21 15:06 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: QEMU Developers

On Fri, 4 Sep 2020 at 10:03, Markus Armbruster <armbru@redhat.com> wrote:
>
> Peter Maydell <peter.maydell@linaro.org> writes:
>
> > Make the handling of indentation in doc comments more sophisticated,

> >          def append(self, line):
> > +            # Strip leading spaces corresponding to the expected indent level
> > +            # Blank lines are always OK.
> > +            if line:
> > +                spacecount = len(line) - len(line.lstrip(" "))
>
> Works, but I'd prefer
>
>                    indent = re.match(r'\s*', line).end()

OK.

> > +                if spacecount > self._indent:
> > +                    spacecount = self._indent
> > +                if spacecount < self._indent:
> > +                    raise QAPIParseError(self._parser, "unexpected de-indent")
>
> New error needs test coverage.  I append a possible test.
>
> Reporting the expected indentation might be helpful.

Fixed; new message produces reports like:
doc-bad-indent.json:6:1: unexpected de-indent (expected at least 4 spaces)

(I have not special-cased "1 spaces" -> "1 space"...)

> > +                line = line[spacecount:]
>
> If you use self._indent instead of spacecount here (which I find
> clearer), you don't need to cap spacecount at self._indent above.

Fixed.

> > +

> > @@ -460,7 +485,17 @@ class QAPIDoc:
> >
> >          if name.startswith('@') and name.endswith(':'):
> >              line = line[len(name)+1:]
> > -            self._start_features_section(name[1:-1])
> > +            if not line or line.isspace():
> > +                # Line is just the "@name:" header, no ident for following lines
>
> pycodestyle complains:
> scripts/qapi/parser.py:489:80: E501 line too long (80 > 79 characters)

Fixed.

> > +                indent = 0
> > +                line = ''
> > +            else:
> > +                # Line is "@arg: first line of description"; following
> > +                # lines should be indented by len(name) + 3, and we
> > +                # pad out this first line so it is handled the same way
> > +                indent = len(name) + 1
>
> Comment claims + 3, code uses + 1.

Yeah. This is because at this point 'name' is not actually just the
name "arg" but includes the leading '@' and trailing ':' so I got
confused between "we want the length of the name ("arg") plus 3"
and the expression you need to actually use. I got this right in the
comment in _append_args_line() but not in _append_features_line().
Will clarify (in both functions) to:

                # Line is "@arg: first line of description"; since 'name'
                # at this point is "@arg:" any following lines should be
                # indented by len(name) + 1. We pad out this first line
                # so it is handled the same way.

> Does this do the right thing when @arg: is followed by multiple
> whitespace characters?

The assumption is that if you added extra whitespace characters that's
because you wanted to specify a line of rST which starts with leading
spaces. So the handling here is that if you say

@foo:   bar
      baz

it's because you want the rST to be

  bar
baz

If this turns out to be invalid rST then the rST parser will
find that out later on.

As it happens I'm not sure whether there is any useful rST
syntax which has leading spaces and where you'd want to be able
to start an argument docstring with it, but it means we're
consistent with our handling of free-form doc comments, where
writing

   Foo
   bar

and writing

Foo
bar

are different things. Also with the change you suggest later
to avoid special-casing the "Examples" section then literal
text becomes an example of where it makes a difference.

> > +                line = ' ' * indent + line
> > +            self._start_features_section(name[1:-1], indent)
> >          elif self._is_section_tag(name):
> >              self._append_line = self._append_various_line
> >              self._append_various_line(line)
> > @@ -493,11 +528,23 @@ class QAPIDoc:
> >                                   % (name, self.sections[0].name))
> >          if self._is_section_tag(name):
> >              line = line[len(name)+1:]
> > -            self._start_section(name[:-1])
> > +            if not line or line.isspace():
> > +                # Line is just "SectionName:", no indent for following lines
> > +                indent = 0
> > +                line = ''
> > +            elif name.startswith("Example"):
> > +                # The "Examples" section is literal-text, so preserve
> > +                # all the indentation as-is
> > +                indent = 0
>
> Section "Example" is an exception.  Needs to be documented. Do we
> really need the exception?  As far as I can see, it's only ever used in
> documentation of block-latency-histogram-set.

Hmm, so you'd rather we changed the documentation of that
command so that instead of

# Example: remove all latency histograms:
#
# -> { "execute": "block-latency-histogram-set",
#      "arguments": { "id": "drive0" } }
# <- { "return": {} }

it would be

# Example:
# remove all latency histograms:
#
# -> { "execute": "block-latency-histogram-set",
#      "arguments": { "id": "drive0" } }
# <- { "return": {} }

and remove the special-case for "Example" so that if you did
write

Example: something on the same line
         more stuff here

it would be treated as literal text

something on the same line
more stuff here

?

Seems reasonable. (I think I put this special case in only
because I was trying to avoid changes to the existing doc
comments if it was easy to accommodate them in the parser.)
That command does seem to be the only outlier, so I've added
a patch to v6 which will fix up its documentation comment
and dropped the special casing.

> > +            else:
> > +                # Line is "SectionName: some text", indent required
>
> Same situation as above, much terser comment.

Fixed to use the expanded comment from earlier.

> > +                indent = len(name) + 1
> > +                line = ' ' * indent + line
> > +            self._start_section(name[:-1], indent)
> >
> >          self._append_freeform(line)

> > @@ -543,7 +590,7 @@ class QAPIDoc:
> >      def connect_member(self, member):
> >          if member.name not in self.args:
> >              # Undocumented TODO outlaw
> > -            self.args[member.name] = QAPIDoc.ArgSection(member.name)
> > +            self.args[member.name] = QAPIDoc.ArgSection(self._parser, member.name)
>
> pycodestyle complains:
> scripts/qapi/parser.py:593:80: E501 line too long (82 > 79 characters)

Fixed.

> >          self.args[member.name].connect(member)
> >
> >      def connect_feature(self, feature):
> > @@ -551,6 +598,8 @@ class QAPIDoc:
> >              raise QAPISemError(feature.info,
> >                                 "feature '%s' lacks documentation"
> >                                 % feature.name)
> > +            self.features[feature.name] = QAPIDoc.ArgSection(self._parser,
> > +                                                             feature.name)
>
> pylint points out:
> scripts/qapi/parser.py:601:12: W0101: Unreachable code (unreachable)
>

Yeah; this part of the patch used to be a "just update all the
callsites of QAPIDoc.ArgSection() to pass the extra argument"
hunk. It looks like your commit 8ec0e1a4e68781 removed this
callsite entirely as dead code, but I missed that in the rebase
and accidentally reintroduced the dead code. Fixed.

> Suggested new test doc-bad-deintent.json, cribbed from your PATCH 06 of
> doc-good.json:
>
> ##
> # @Alternate:
> # @i: an integer
> # @b is undocumented
> ##
> { 'alternate': 'Alternate',
>   'data': { 'i': 'int', 'b': 'bool' } }

The '@' at the front of the second line here is not relevant to
the mis-indentation and it's kind of confusing (as the correct
fix is "add a colon", not "reindent the line"), so I think I'd
rather have a test that's clearly looking at the indent:

# Multiline doc comments should have consistent indentation

##
# @foo:
# @a: line one
# line two is wrongly indented
##
{ 'command': 'foo', 'data': { 'a': 'int' } }

which expects the error:

doc-bad-indent.json:6:1: unexpected de-indent (expected at least 4 spaces)

thanks
-- PMM


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

* Re: [PATCH v5 10/20] docs/interop: Convert qemu-ga-ref to rST
  2020-09-04 13:16   ` Markus Armbruster
  2020-09-04 13:18     ` Peter Maydell
@ 2020-09-21 15:30     ` Peter Maydell
  2020-09-22 12:00       ` Markus Armbruster
  1 sibling, 1 reply; 62+ messages in thread
From: Peter Maydell @ 2020-09-21 15:30 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: QEMU Developers

On Fri, 4 Sep 2020 at 14:16, Markus Armbruster <armbru@redhat.com> wrote:
>
> Peter Maydell <peter.maydell@linaro.org> writes:
>
> > Convert qemu-ga-ref to rST format. This includes dropping
> > the plain-text, pdf and info format outputs for this document;
> > as with all our other Sphinx-based documentation, we provide
> > HTML and manpage only.

> > -@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
>
> Does the interop manual carry an equivalent copyright notice?

NB: in the Texinfo versions, Texinfo does not put this copyright/license
notice in its generated manpages, and it is in the generated HTML only
as an HTML comment, not visible to the ordinary reader.

The Sphinx interop manual has the usual footer:
https://www.qemu.org/docs/master/interop/index.html
"©2020, The QEMU Project Developers."

The system manual has an explicit "License" section:
https://www.qemu.org/docs/master/system/license.html
but that's documenting the license of the program, not the
manual (it's docs/system/license.rst).

We could do any or all of:
 * decide that we're happy with the current situation
 * expand the "copyright" footer to something like
   '©2020, The QEMU Project Developers; this manual is GPLv2'
 * have a docs/foo/license.rst for each manual, and expand
   it to mention the documentation license as well as the
   code license

Given that the Texinfo generated QMP/QGA references don't
actually present this text to the reader, my inclination
is to say that this is something we should address in
a separate patchseries, not as part of this conversion.

thanks
-- PMM


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

* Re: [PATCH v5 09/20] docs/sphinx: Add new qapi-doc Sphinx extension
  2020-09-04 14:44   ` Markus Armbruster
  2020-09-04 14:52     ` Peter Maydell
@ 2020-09-21 16:50     ` Peter Maydell
  2020-09-22 11:47       ` Markus Armbruster
  1 sibling, 1 reply; 62+ messages in thread
From: Peter Maydell @ 2020-09-21 16:50 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: QEMU Developers

On Fri, 4 Sep 2020 at 15:44, Markus Armbruster <armbru@redhat.com> wrote:
>
> $ pycodestyle docs/sphinx/qapidoc.py
> docs/sphinx/qapidoc.py:42:1: E302 expected 2 blank lines, found 1
> docs/sphinx/qapidoc.py:50:1: E302 expected 2 blank lines, found 1
> docs/sphinx/qapidoc.py:74:80: E501 line too long (80 > 79 characters)
> docs/sphinx/qapidoc.py:388:80: E501 line too long (80 > 79 characters)
> docs/sphinx/qapidoc.py:391:80: E501 line too long (80 > 79 characters)
> docs/sphinx/qapidoc.py:430:1: E302 expected 2 blank lines, found 1
> docs/sphinx/qapidoc.py:489:80: E501 line too long (80 > 79 characters)
> docs/sphinx/qapidoc.py:495:1: E302 expected 2 blank lines, found 1

All fixed.

> $ PYTHONPATH=scripts pylint docs/sphinx/qapidoc.py
> ************* Module qapidoc
> docs/sphinx/qapidoc.py:36:4: E0611: No name 'AutodocReporter' in module 'sphinx.ext.autodoc' (no-name-in-module)
> docs/sphinx/qapidoc.py:45:10: R1708: Do not raise StopIteration in generator, use return statement instead (stop-iteration-return)
> docs/sphinx/qapidoc.py:104:4: R0201: Method could be a function (no-self-use)
> docs/sphinx/qapidoc.py:253:4: R0201: Method could be a function (no-self-use)
> docs/sphinx/qapidoc.py:34:4: C0412: Imports from package sphinx are not grouped (ungrouped-imports)

Not fixed: I disagree with the linter on all these.

The first and fifth of these are unfixable because they are the
result of code that is trying to adapt to multiple versions of
Sphinx (one of which has AutodocReporter and the other of which
does not).

The second makes no sense and appears to me to be a linter
bug, because the code doesn't (directly) raise StopIteration.
In any case the function being complained about is just a
straight borrowing from pydash.

The third and fourth would mean that two of the 10 or so
_nodes_for_whatever methods would become functions and
gratuitously different in call signature from the rest
just because it happens that the current implementation
doesn't need 'self'.

(The version of pylint I have also warns about:
 * comments that say "TODO", both of which are ones that
   are carried over from the texinfo generator about dropping
   fallback code when undocumented members are outlawed
 * methods that are part of the QAPISchemaVisitor
   interface and apparently have "too many arguments")
 * single-letter variables
 * the Use_SSI variable name which is from the kerneldoc plugin
I'm going to ignore those too.)

thanks
-- PMM


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

* Re: [PATCH v5 09/20] docs/sphinx: Add new qapi-doc Sphinx extension
  2020-09-04 12:29   ` Markus Armbruster
@ 2020-09-21 18:06     ` Peter Maydell
  2020-09-22 11:42       ` Markus Armbruster
  2020-09-24 16:30       ` Peter Maydell
  0 siblings, 2 replies; 62+ messages in thread
From: Peter Maydell @ 2020-09-21 18:06 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: QEMU Developers

On Fri, 4 Sep 2020 at 13:29, Markus Armbruster <armbru@redhat.com> wrote:
>
> Peter Maydell <peter.maydell@linaro.org> writes:
>
> > Some of our documentation is auto-generated from documentation
> > comments in the JSON schema.
> >
> > For Sphinx, rather than creating a file to include, the most natural
> > way to handle this is to have a small custom Sphinx extension which
> > processes the JSON file and inserts documentation into the rST
> > file being processed.
> >
> > This is the same approach that kerneldoc and hxtool use.
> >
> > Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
>
> Got a pointer to "Writing Sphinx Extensions for Dummies" or similar?

The upstream documentation on extending Sphinx is probably
a good place to start; in particular the tutorials are useful:

https://www.sphinx-doc.org/en/master/development/index.html

I have also found that a fair amount of trial-and-error,
examination of the source of Sphinx itself or of other
extensions, and occasionally asking questions on the Sphinx
mailing list has been necessary...

> > --- /dev/null
> > +++ b/docs/sphinx/qapidoc.py
> > @@ -0,0 +1,504 @@
> > +# coding=utf-8
> > +#
> > +# QEMU qapidoc QAPI file parsing extension
> > +#
> > +# Copyright (c) 2020 Linaro
> > +#
> > +# This work is licensed under the terms of the GNU GPLv2 or later.
> > +# See the COPYING file in the top-level directory.
> > +"""qapidoc is a Sphinx extension that implements the qapi-doc directive"""
> > +
> > +# The purpose of this extension is to read the documentation comments
> > +# in QAPI JSON schema files, and insert them all into the current document.
>
> Let's drop "JSON", it's not really true.

Fixed.

> > +# The conf.py file must set the qapidoc_srctree config value to
> > +# the root of the QEMU source tree.
> > +# Each qapi-doc:: directive takes one argument which is the
> > +# path of the .json file to process, relative to the source tree.
>
> Beg your pardon?  Oh, you're talking about .rst files now.  The next two
> patches will add such files.  Perhaps something like "The qapi-doc::
> reST directive provided by this extension takes ..."

OK, how about:

# The purpose of this extension is to read the documentation comments
# in QAPI schema files, and insert them all into the current document.
#
# It implements one new rST directive, "qapi-doc::".
# Each qapi-doc:: directive takes one argument, which is the
# path of the .json file to process, relative to the source tree.
#
# The docs/conf.py file must set the qapidoc_srctree config value to
# the root of the QEMU source tree.

?

> > +# Function borrowed from pydash, which is under the MIT license
> > +def intersperse(iterable, separator):
> > +    """Like join, but for arbitrary iterables, notably arrays"""
> > +    iterable = iter(iterable)
> > +    yield next(iterable)
> > +    for item in iterable:
> > +        yield separator
> > +        yield item
>
> What's wrong with separator.join(iterable)?

It doesn't work on all the things we'd like to intersperse().
If you try defining intersperse as

def intersperse(iterable, separator):
    separator.join(iterable)

then you get an exception:

  File "/home/petmay01/linaro/qemu-from-laptop/qemu/docs/../scripts/qapi/schema.py",
line 445, in visit
    self.base, self.local_members, self.variants)
  File "/home/petmay01/linaro/qemu-from-laptop/qemu/docs/sphinx/qapidoc.py",
line 314, in visit_object_type
    [self._nodes_for_members(doc, 'Members', base, variants),
  File "/home/petmay01/linaro/qemu-from-laptop/qemu/docs/sphinx/qapidoc.py",
line 173, in _nodes_for_members
    nodes.Text(', ')))
  File "/home/petmay01/linaro/qemu-from-laptop/qemu/docs/sphinx/qapidoc.py",
line 53, in intersperse
    separator.join(iterable)
TypeError: sequence item 0: expected str instance, literal found

> > +
> > +class QAPISchemaGenRSTVisitor(QAPISchemaVisitor):
> > +    """A QAPI schema visitor which generates docutils/Sphinx nodes
> > +
> > +    This class builds up a tree of docutils/Sphinx nodes corresponding
> > +    to documentation for the various QAPI objects. To use it, first create
> > +    a QAPISchemaGenRSTVisitor object, and call its visit_begin() method.
> > +    Then you can call one of the two methods 'freeform' (to add documentation
> > +    for a freeform documentation chunk) or 'symbol' (to add documentation
> > +    for a QAPI symbol). These will cause the visitor to build up the
> > +    tree of document nodes. Once you've added all the documentation
> > +    via 'freeform' and 'symbol' method calls, you can call 'get_document_nodes'
> > +    to get the final list of document nodes (in a form suitable for returning
> > +    from a Sphinx directive's 'run' method).
> > +    """
> > +    def __init__(self, sphinx_directive):
> > +        self._cur_doc = None
> > +        self._sphinx_directive = sphinx_directive
> > +        self._top_node = nodes.section()
> > +        self._active_headings = [self._top_node]
> > +
> > +    def _serror(self, msg):
> > +        """Raise an exception giving a user-friendly syntax error message"""
> > +        file = self._cur_doc.info.fname
> > +        line = self._cur_doc.info.line
> > +        raise ExtensionError('%s line %d: syntax error: %s' % (file, line, msg))
>
> Note for later: ornate error message format, no use of QAPISourceInfo.
>
> > +

> > +
> > +    def _nodes_for_enum_values(self, doc, what):
>
> @what is only ever 'Values', isn't it?

Yes. I think this was a legacy from conversion from scripts/qapi/doc.py,
which does pass in a 'Values' string, but it does that because it shares
code in texi_members() between enums and other things. In this code
I chose to split that out into different functions rather than using
one function with a member_func argument because the member vs enum
code is different enough that it's clearer that way. But I didn't
notice that there wasn't any longer much point in passing a 'what'
argument.

Fixed to hard-code 'Values' rather than passing it as an argument.

> > +    def _nodes_for_sections(self, doc, ifcond):
> > +        """Return doctree nodes for additional sections following arguments"""
> > +        nodelist = []
> > +        for section in doc.sections:
> > +            snode = self._make_section(section.name)
> > +            if section.name and section.name.startswith('Example'):
> > +                snode += self._nodes_for_example(section.text)
> > +            else:
> > +                self._parse_text_into_node(section.text, snode)
> > +            nodelist.append(snode)
> > +        if ifcond:
> > +            snode = self._make_section('If')
> > +            snode += self._nodes_for_ifcond(ifcond, with_if=False)
> > +            nodelist.append(snode)
>
> I think I'd rather have a separate _node_for_ifcond().  Not a demand.

I'm not sure what you have in mind here, so I'll leave it as it is :-)

> > +        if not nodelist:
> > +            return None
>
> Any particular reason for mapping [] to None?

I forget at this point. Probably related to the next query...

> > +        return nodelist
> > +
> > +    def _add_doc(self, typ, sections):
> > +        """Add documentation for a command/object/enum...
> > +
> > +        We assume we're documenting the thing defined in self._cur_doc.
> > +        typ is the type of thing being added ("Command", "Object", etc)
> > +
> > +        sections is a list of nodes for sections to add to the definition.
> > +        """
> > +
> > +        doc = self._cur_doc
> > +        snode = nodes.section(ids=[self._sphinx_directive.new_serialno()])
> > +        snode += nodes.title('', '', *[nodes.literal(doc.symbol, doc.symbol),
> > +                                       nodes.Text(' (' + typ + ')')])
> > +        self._parse_text_into_node(doc.body.text, snode)
> > +        for s in sections:
> > +            if s is not None:
> > +                snode += s
>
> Looks like you're flattening the two-level list the callers create,
> e.g. ...
>
> > +        self._add_node_to_current_heading(snode)
> > +
> > +    def visit_enum_type(self, name, info, ifcond, features, members, prefix):
> > +        doc = self._cur_doc
> > +        self._add_doc('Enum',
> > +                      [self._nodes_for_enum_values(doc, 'Values'),
> > +                       self._nodes_for_features(doc),
> > +                       self._nodes_for_sections(doc, ifcond)])
>
> ... this one: [[nodes for enum values...]
>                [nodes for features...]
>                [nodes for sections]]
>
> Makes me wonder why you build two levels in the first place.

This is probably just me not being a very experienced Python
programmer. What's the syntax for passing _add_doc the single list
which is just the concatenation of the various lists that
the _nodes_for_* methods return ?

> > +
> > +        if re.match(r'=+ ', doc.body.text):
> > +            # Section or subsection heading: must be the only thing in the block
> > +            (heading, _, rest) = doc.body.text.partition('\n')
> > +            if rest != '':
> > +                raise ExtensionError('%s line %s: section or subsection heading'
> > +                                     ' must be in its own doc comment block'
> > +                                     % (doc.info.fname, doc.info.line))
>
> Same ornate error message format as above, less 'syntax error: '.

Yes. I realized after sending v5 that this could be made to
call _serror() instead of directly raising the ExtensionError.

> > +        try:
> > +            schema = QAPISchema(qapifile)
> > +        except QAPIError as err:
> > +            # Launder QAPI parse errors into Sphinx extension errors
> > +            # so they are displayed nicely to the user
> > +            raise ExtensionError(str(err))
>
> I expected plumbing the error location through Sphinx to the user to
> take a bit more effort.  I'm not complaining.
>
> A QAPIError looks like this when converted to str:
>
>     In file included from ...:
>     ...
>     In file included from ...:
>     FILE: In ENTITY-TYPE 'NAME':
>     FILE:LINE: MESSAGE
>
> "In file" lines appear when the error is in a sub-module.
>
> An "In ENTITY-TYPE" line appears when the error is in an entity
> definition.
>
> The other two ExtensionError() look like
>
>     FILE line LINE: syntax error: MESSAGE
>
> and
>
>     FILE line LINE: MESSAGE
>
> More ornate, less information.
>
> I figure more errors can get thrown by nested_parse_with_titles() (see
> below).  How do they look like?

They look like this:

Warning, treated as error:
/home/petmay01/linaro/qemu-from-laptop/qemu/docs/../qapi/block.json:15:Bullet
list ends without a blank line; unexpected unindent.

(I've just noticed that with Sphinx 1.6, which we still have
to support, the file/line info isn't passed through, so you get:

Warning, treated as error:
/home/petmay01/linaro/qemu-from-laptop/qemu/docs/interop/qemu-qmp-ref.rst:7:Bullet
list ends without a blank line; unexpected unindent.

The plugin has code borrowed from kerneldoc.py which is
*supposed* to handle the older API Sphinx 1.6 used, but it
looks like it's broken. I'll have a look and see if it
is fixable, but possibly we may have to live with people
developing on old distros getting suboptimal errors.)

> Can we avoid the information loss?
>
> Can we make the error message format more consistent?

How do I get a QAPIError from within one of the
visit_* or freeform methods of a QAPISchemaVisitor ?
The current texinfo doc generator doesn't do anything like that.
If there's a way to get a QAPIError given
 * the 'doc' argument passed into freeform/visit*
 * the specific error message we want to emit
then we can make the errors this script itself emits
(which are just the ones about headings: wrongly-subnested
headings, and headings that aren't the only thing in their
freeform-doc block) consistent with the other QAPIError ones.
[As I mentioned in some earlier thread, we are identifying
these errors here for pragmatic "it was easy" reasons,
though you could make a case that scripts/qapi/parser.py
should be doing it.]

(Aside: what was your conclusion on the question of section
headers, anyway? I need to go back and re-read the older
threads but I think that is still an unresolved topic...)

However I suspect we can't generate QAPIErrors for errors which are
produced by Sphinx itself when it's handed invalid rST:
the API Sphinx provides us is that we can annotate the lines
of rST that we are assembling from the input files to indicate
their "true" file/line number, but we can't intercept error
messages it emits. Think of it like the C compiler -- a
preprocessor can add #line directives to pass fixed up file/line
info to the compiler, but it isn't in the loop when the compiler
actually prints out errors.

> I had to read mostly backwards to understand the code.

Yes. This seems to be the standard way to write a Sphinx extension.
(Compare the kerneldoc.py and the various example extensions
in the Sphinx docs.)

thanks
-- PMM


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

* Re: [PATCH v5 08/20] scripts/qapi/parser.py: improve doc comment indent handling
  2020-09-21 15:06     ` Peter Maydell
@ 2020-09-22  7:27       ` Markus Armbruster
  2020-09-22 11:48         ` Peter Maydell
  0 siblings, 1 reply; 62+ messages in thread
From: Markus Armbruster @ 2020-09-22  7:27 UTC (permalink / raw)
  To: Peter Maydell; +Cc: QEMU Developers

Peter Maydell <peter.maydell@linaro.org> writes:

> On Fri, 4 Sep 2020 at 10:03, Markus Armbruster <armbru@redhat.com> wrote:
>>
>> Peter Maydell <peter.maydell@linaro.org> writes:
>>
>> > Make the handling of indentation in doc comments more sophisticated,
>
>> >          def append(self, line):
>> > +            # Strip leading spaces corresponding to the expected indent level
>> > +            # Blank lines are always OK.
>> > +            if line:
>> > +                spacecount = len(line) - len(line.lstrip(" "))
>>
>> Works, but I'd prefer
>>
>>                    indent = re.match(r'\s*', line).end()
>
> OK.
>
>> > +                if spacecount > self._indent:
>> > +                    spacecount = self._indent
>> > +                if spacecount < self._indent:
>> > +                    raise QAPIParseError(self._parser, "unexpected de-indent")
>>
>> New error needs test coverage.  I append a possible test.
>>
>> Reporting the expected indentation might be helpful.
>
> Fixed; new message produces reports like:
> doc-bad-indent.json:6:1: unexpected de-indent (expected at least 4 spaces)
>
> (I have not special-cased "1 spaces" -> "1 space"...)
>
>> > +                line = line[spacecount:]
>>
>> If you use self._indent instead of spacecount here (which I find
>> clearer), you don't need to cap spacecount at self._indent above.
>
> Fixed.
>
>> > +
>
>> > @@ -460,7 +485,17 @@ class QAPIDoc:
>> >
>> >          if name.startswith('@') and name.endswith(':'):
>> >              line = line[len(name)+1:]
>> > -            self._start_features_section(name[1:-1])
>> > +            if not line or line.isspace():
>> > +                # Line is just the "@name:" header, no ident for following lines
>>
>> pycodestyle complains:
>> scripts/qapi/parser.py:489:80: E501 line too long (80 > 79 characters)
>
> Fixed.
>
>> > +                indent = 0
>> > +                line = ''
>> > +            else:
>> > +                # Line is "@arg: first line of description"; following
>> > +                # lines should be indented by len(name) + 3, and we
>> > +                # pad out this first line so it is handled the same way
>> > +                indent = len(name) + 1
>>
>> Comment claims + 3, code uses + 1.
>
> Yeah. This is because at this point 'name' is not actually just the
> name "arg" but includes the leading '@' and trailing ':' so I got
> confused between "we want the length of the name ("arg") plus 3"
> and the expression you need to actually use. I got this right in the
> comment in _append_args_line() but not in _append_features_line().
> Will clarify (in both functions) to:
>
>                 # Line is "@arg: first line of description"; since 'name'
>                 # at this point is "@arg:" any following lines should be
>                 # indented by len(name) + 1. We pad out this first line
>                 # so it is handled the same way.
>
>> Does this do the right thing when @arg: is followed by multiple
>> whitespace characters?
>
> The assumption is that if you added extra whitespace characters that's
> because you wanted to specify a line of rST which starts with leading
> spaces. So the handling here is that if you say
>
> @foo:   bar
>       baz
>
> it's because you want the rST to be
>
>   bar
> baz
>
> If this turns out to be invalid rST then the rST parser will
> find that out later on.

In general, I'm wary of making the amount of whitespace within a line
significant, but in this case, the visual misalignment of bar and baz
should make accidents unlikely.

How does

  @foo:  bar
         baz
  @frob: gnu
         gnat

behave?

This is something people may actually write.

> As it happens I'm not sure whether there is any useful rST
> syntax which has leading spaces and where you'd want to be able
> to start an argument docstring with it, but it means we're
> consistent with our handling of free-form doc comments, where
> writing
>
>    Foo
>    bar
>
> and writing
>
> Foo
> bar
>
> are different things. Also with the change you suggest later
> to avoid special-casing the "Examples" section then literal
> text becomes an example of where it makes a difference.

Valid points.

>> > +                line = ' ' * indent + line
>> > +            self._start_features_section(name[1:-1], indent)
>> >          elif self._is_section_tag(name):
>> >              self._append_line = self._append_various_line
>> >              self._append_various_line(line)
>> > @@ -493,11 +528,23 @@ class QAPIDoc:
>> >                                   % (name, self.sections[0].name))
>> >          if self._is_section_tag(name):
>> >              line = line[len(name)+1:]
>> > -            self._start_section(name[:-1])
>> > +            if not line or line.isspace():
>> > +                # Line is just "SectionName:", no indent for following lines
>> > +                indent = 0
>> > +                line = ''
>> > +            elif name.startswith("Example"):
>> > +                # The "Examples" section is literal-text, so preserve
>> > +                # all the indentation as-is
>> > +                indent = 0
>>
>> Section "Example" is an exception.  Needs to be documented. Do we
>> really need the exception?  As far as I can see, it's only ever used in
>> documentation of block-latency-histogram-set.
>
> Hmm, so you'd rather we changed the documentation of that
> command so that instead of
>
> # Example: remove all latency histograms:
> #
> # -> { "execute": "block-latency-histogram-set",
> #      "arguments": { "id": "drive0" } }
> # <- { "return": {} }
>
> it would be
>
> # Example:
> # remove all latency histograms:
> #
> # -> { "execute": "block-latency-histogram-set",
> #      "arguments": { "id": "drive0" } }
> # <- { "return": {} }
>
> and remove the special-case for "Example" so that if you did
> write
>
> Example: something on the same line
>          more stuff here
>
> it would be treated as literal text
>
> something on the same line
> more stuff here
>
> ?
>
> Seems reasonable. (I think I put this special case in only
> because I was trying to avoid changes to the existing doc
> comments if it was easy to accommodate them in the parser.)
> That command does seem to be the only outlier, so I've added
> a patch to v6 which will fix up its documentation comment
> and dropped the special casing.

Sounds like a good trade.

>> > +            else:
>> > +                # Line is "SectionName: some text", indent required
>>
>> Same situation as above, much terser comment.
>
> Fixed to use the expanded comment from earlier.
>
>> > +                indent = len(name) + 1
>> > +                line = ' ' * indent + line
>> > +            self._start_section(name[:-1], indent)
>> >
>> >          self._append_freeform(line)
>
>> > @@ -543,7 +590,7 @@ class QAPIDoc:
>> >      def connect_member(self, member):
>> >          if member.name not in self.args:
>> >              # Undocumented TODO outlaw
>> > -            self.args[member.name] = QAPIDoc.ArgSection(member.name)
>> > +            self.args[member.name] = QAPIDoc.ArgSection(self._parser, member.name)
>>
>> pycodestyle complains:
>> scripts/qapi/parser.py:593:80: E501 line too long (82 > 79 characters)
>
> Fixed.
>
>> >          self.args[member.name].connect(member)
>> >
>> >      def connect_feature(self, feature):
>> > @@ -551,6 +598,8 @@ class QAPIDoc:
>> >              raise QAPISemError(feature.info,
>> >                                 "feature '%s' lacks documentation"
>> >                                 % feature.name)
>> > +            self.features[feature.name] = QAPIDoc.ArgSection(self._parser,
>> > +                                                             feature.name)
>>
>> pylint points out:
>> scripts/qapi/parser.py:601:12: W0101: Unreachable code (unreachable)
>>
>
> Yeah; this part of the patch used to be a "just update all the
> callsites of QAPIDoc.ArgSection() to pass the extra argument"
> hunk. It looks like your commit 8ec0e1a4e68781 removed this
> callsite entirely as dead code, but I missed that in the rebase
> and accidentally reintroduced the dead code. Fixed.
>
>> Suggested new test doc-bad-deintent.json, cribbed from your PATCH 06 of
>> doc-good.json:
>>
>> ##
>> # @Alternate:
>> # @i: an integer
>> # @b is undocumented
>> ##
>> { 'alternate': 'Alternate',
>>   'data': { 'i': 'int', 'b': 'bool' } }
>
> The '@' at the front of the second line here is not relevant to
> the mis-indentation and it's kind of confusing (as the correct
> fix is "add a colon", not "reindent the line"), so I think I'd
> rather have a test that's clearly looking at the indent:
>
> # Multiline doc comments should have consistent indentation
>
> ##
> # @foo:
> # @a: line one
> # line two is wrongly indented
> ##
> { 'command': 'foo', 'data': { 'a': 'int' } }
>
> which expects the error:
>
> doc-bad-indent.json:6:1: unexpected de-indent (expected at least 4 spaces)

Yes, that's better.



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

* Re: [PATCH v5 09/20] docs/sphinx: Add new qapi-doc Sphinx extension
  2020-09-21 18:06     ` Peter Maydell
@ 2020-09-22 11:42       ` Markus Armbruster
  2020-09-24 13:25         ` Peter Maydell
  2020-09-24 16:30       ` Peter Maydell
  1 sibling, 1 reply; 62+ messages in thread
From: Markus Armbruster @ 2020-09-22 11:42 UTC (permalink / raw)
  To: Peter Maydell; +Cc: QEMU Developers

Peter Maydell <peter.maydell@linaro.org> writes:

> On Fri, 4 Sep 2020 at 13:29, Markus Armbruster <armbru@redhat.com> wrote:
>>
>> Peter Maydell <peter.maydell@linaro.org> writes:
>>
>> > Some of our documentation is auto-generated from documentation
>> > comments in the JSON schema.
>> >
>> > For Sphinx, rather than creating a file to include, the most natural
>> > way to handle this is to have a small custom Sphinx extension which
>> > processes the JSON file and inserts documentation into the rST
>> > file being processed.
>> >
>> > This is the same approach that kerneldoc and hxtool use.
>> >
>> > Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
>>
>> Got a pointer to "Writing Sphinx Extensions for Dummies" or similar?
>
> The upstream documentation on extending Sphinx is probably
> a good place to start; in particular the tutorials are useful:
>
> https://www.sphinx-doc.org/en/master/development/index.html

Do you think having this link in a comment could help future readers /
maintainers?

> I have also found that a fair amount of trial-and-error,
> examination of the source of Sphinx itself or of other
> extensions, and occasionally asking questions on the Sphinx
> mailing list has been necessary...
>
>> > --- /dev/null
>> > +++ b/docs/sphinx/qapidoc.py
>> > @@ -0,0 +1,504 @@
>> > +# coding=utf-8
>> > +#
>> > +# QEMU qapidoc QAPI file parsing extension
>> > +#
>> > +# Copyright (c) 2020 Linaro
>> > +#
>> > +# This work is licensed under the terms of the GNU GPLv2 or later.
>> > +# See the COPYING file in the top-level directory.
>> > +"""qapidoc is a Sphinx extension that implements the qapi-doc directive"""
>> > +
>> > +# The purpose of this extension is to read the documentation comments
>> > +# in QAPI JSON schema files, and insert them all into the current document.
>>
>> Let's drop "JSON", it's not really true.
>
> Fixed.
>
>> > +# The conf.py file must set the qapidoc_srctree config value to
>> > +# the root of the QEMU source tree.
>> > +# Each qapi-doc:: directive takes one argument which is the
>> > +# path of the .json file to process, relative to the source tree.
>>
>> Beg your pardon?  Oh, you're talking about .rst files now.  The next two
>> patches will add such files.  Perhaps something like "The qapi-doc::
>> reST directive provided by this extension takes ..."
>
> OK, how about:
>
> # The purpose of this extension is to read the documentation comments
> # in QAPI schema files, and insert them all into the current document.
> #
> # It implements one new rST directive, "qapi-doc::".
> # Each qapi-doc:: directive takes one argument, which is the
> # path of the .json file to process, relative to the source tree.
> #
> # The docs/conf.py file must set the qapidoc_srctree config value to
> # the root of the QEMU source tree.
>
> ?

Works for me with "pathname" instead of "path".  I'd also write "schema
file" instead of ".json file", but that's a matter of taste and up to
you.

>> > +# Function borrowed from pydash, which is under the MIT license
>> > +def intersperse(iterable, separator):
>> > +    """Like join, but for arbitrary iterables, notably arrays"""
>> > +    iterable = iter(iterable)
>> > +    yield next(iterable)
>> > +    for item in iterable:
>> > +        yield separator
>> > +        yield item
>>
>> What's wrong with separator.join(iterable)?
>
> It doesn't work on all the things we'd like to intersperse().
> If you try defining intersperse as
>
> def intersperse(iterable, separator):
>     separator.join(iterable)
>
> then you get an exception:
>
>   File "/home/petmay01/linaro/qemu-from-laptop/qemu/docs/../scripts/qapi/schema.py",
> line 445, in visit
>     self.base, self.local_members, self.variants)
>   File "/home/petmay01/linaro/qemu-from-laptop/qemu/docs/sphinx/qapidoc.py",
> line 314, in visit_object_type
>     [self._nodes_for_members(doc, 'Members', base, variants),
>   File "/home/petmay01/linaro/qemu-from-laptop/qemu/docs/sphinx/qapidoc.py",
> line 173, in _nodes_for_members
>     nodes.Text(', ')))
>   File "/home/petmay01/linaro/qemu-from-laptop/qemu/docs/sphinx/qapidoc.py",
> line 53, in intersperse
>     separator.join(iterable)
> TypeError: sequence item 0: expected str instance, literal found

Aha.

@iterable appears to be a list of docutils.node.literal, whatever that
may be.

The doc string is confusing (I figure you borrowed the it along with the
code).  str.join() works just fine for arrays.  It's not the kind of
iterable that's the problem, it's the kind of thing the iterable yields:
str.join() wants str, we have. docutils.node.literal.

The lazy solution is to delete the confusing doc string.

Here's my attempt at a non-lazy solution:

    """Yield the members of *iterable* interspersed with *separator*."""

>> > +
>> > +class QAPISchemaGenRSTVisitor(QAPISchemaVisitor):
>> > +    """A QAPI schema visitor which generates docutils/Sphinx nodes
>> > +
>> > +    This class builds up a tree of docutils/Sphinx nodes corresponding
>> > +    to documentation for the various QAPI objects. To use it, first create
>> > +    a QAPISchemaGenRSTVisitor object, and call its visit_begin() method.
>> > +    Then you can call one of the two methods 'freeform' (to add documentation
>> > +    for a freeform documentation chunk) or 'symbol' (to add documentation
>> > +    for a QAPI symbol). These will cause the visitor to build up the
>> > +    tree of document nodes. Once you've added all the documentation
>> > +    via 'freeform' and 'symbol' method calls, you can call 'get_document_nodes'
>> > +    to get the final list of document nodes (in a form suitable for returning
>> > +    from a Sphinx directive's 'run' method).
>> > +    """
>> > +    def __init__(self, sphinx_directive):
>> > +        self._cur_doc = None
>> > +        self._sphinx_directive = sphinx_directive
>> > +        self._top_node = nodes.section()
>> > +        self._active_headings = [self._top_node]
>> > +
>> > +    def _serror(self, msg):
>> > +        """Raise an exception giving a user-friendly syntax error message"""
>> > +        file = self._cur_doc.info.fname
>> > +        line = self._cur_doc.info.line
>> > +        raise ExtensionError('%s line %d: syntax error: %s' % (file, line, msg))
>>
>> Note for later: ornate error message format, no use of QAPISourceInfo.
>>
>> > +
>
>> > +
>> > +    def _nodes_for_enum_values(self, doc, what):
>>
>> @what is only ever 'Values', isn't it?
>
> Yes. I think this was a legacy from conversion from scripts/qapi/doc.py,
> which does pass in a 'Values' string, but it does that because it shares
> code in texi_members() between enums and other things. In this code
> I chose to split that out into different functions rather than using
> one function with a member_func argument because the member vs enum
> code is different enough that it's clearer that way. But I didn't
> notice that there wasn't any longer much point in passing a 'what'
> argument.
>
> Fixed to hard-code 'Values' rather than passing it as an argument.
>
>> > +    def _nodes_for_sections(self, doc, ifcond):
>> > +        """Return doctree nodes for additional sections following arguments"""
>> > +        nodelist = []
>> > +        for section in doc.sections:
>> > +            snode = self._make_section(section.name)
>> > +            if section.name and section.name.startswith('Example'):
>> > +                snode += self._nodes_for_example(section.text)
>> > +            else:
>> > +                self._parse_text_into_node(section.text, snode)
>> > +            nodelist.append(snode)
>> > +        if ifcond:
>> > +            snode = self._make_section('If')
>> > +            snode += self._nodes_for_ifcond(ifcond, with_if=False)
>> > +            nodelist.append(snode)
>>
>> I think I'd rather have a separate _node_for_ifcond().  Not a demand.
>
> I'm not sure what you have in mind here, so I'll leave it as it is :-)

_nodes_for_sections() is used like this:

        self._add_doc(WHAT,
                      [self._nodes_for_WHAT(doc, ...),
                       self._nodes_for_features(doc),
                       self._nodes_for_sections(doc, ifcond)])

._add_doc()'s second argument is a list of nodes for sections.

._nodes_for_WHAT() computes the nodes specific to the kind of thing
we're processing: enum, object, alternate, command, event.

._nodes_for_features() computes the nodes for the feature section.

._nodes_for_sections() computes both the nodes for additional sections
and the nodes for the ifcond section.

I'd compute the nodes for the ifcond section in their own function, to
keep .nodes_for_sections() focused on just one purpose.

>> > +        if not nodelist:
>> > +            return None
>>
>> Any particular reason for mapping [] to None?
>
> I forget at this point. Probably related to the next query...
>
>> > +        return nodelist
>> > +
>> > +    def _add_doc(self, typ, sections):
>> > +        """Add documentation for a command/object/enum...
>> > +
>> > +        We assume we're documenting the thing defined in self._cur_doc.
>> > +        typ is the type of thing being added ("Command", "Object", etc)
>> > +
>> > +        sections is a list of nodes for sections to add to the definition.
>> > +        """
>> > +
>> > +        doc = self._cur_doc
>> > +        snode = nodes.section(ids=[self._sphinx_directive.new_serialno()])
>> > +        snode += nodes.title('', '', *[nodes.literal(doc.symbol, doc.symbol),
>> > +                                       nodes.Text(' (' + typ + ')')])
>> > +        self._parse_text_into_node(doc.body.text, snode)
>> > +        for s in sections:
>> > +            if s is not None:
>> > +                snode += s
>>
>> Looks like you're flattening the two-level list the callers create,
>> e.g. ...
>>
>> > +        self._add_node_to_current_heading(snode)
>> > +
>> > +    def visit_enum_type(self, name, info, ifcond, features, members, prefix):
>> > +        doc = self._cur_doc
>> > +        self._add_doc('Enum',
>> > +                      [self._nodes_for_enum_values(doc, 'Values'),
>> > +                       self._nodes_for_features(doc),
>> > +                       self._nodes_for_sections(doc, ifcond)])
>>
>> ... this one: [[nodes for enum values...]
>>                [nodes for features...]
>>                [nodes for sections]]
>>
>> Makes me wonder why you build two levels in the first place.
>
> This is probably just me not being a very experienced Python
> programmer. What's the syntax for passing _add_doc the single list
> which is just the concatenation of the various lists that
> the _nodes_for_* methods return ?

You could replace

                      [self._nodes_for_WHAT(doc, ...),
                       self._nodes_for_features(doc),
                       self._nodes_for_sections(doc, ifcond)])

by

                      self._nodes_for_WHAT(doc, ...)
                      + self._nodes_for_features(doc)
                      + self._nodes_for_sections(doc, ifcond)

Operator + concatenates sequences.

>> > +
>> > +        if re.match(r'=+ ', doc.body.text):
>> > +            # Section or subsection heading: must be the only thing in the block
>> > +            (heading, _, rest) = doc.body.text.partition('\n')
>> > +            if rest != '':
>> > +                raise ExtensionError('%s line %s: section or subsection heading'
>> > +                                     ' must be in its own doc comment block'
>> > +                                     % (doc.info.fname, doc.info.line))
>>
>> Same ornate error message format as above, less 'syntax error: '.
>
> Yes. I realized after sending v5 that this could be made to
> call _serror() instead of directly raising the ExtensionError.
>
>> > +        try:
>> > +            schema = QAPISchema(qapifile)
>> > +        except QAPIError as err:
>> > +            # Launder QAPI parse errors into Sphinx extension errors
>> > +            # so they are displayed nicely to the user
>> > +            raise ExtensionError(str(err))
>>
>> I expected plumbing the error location through Sphinx to the user to
>> take a bit more effort.  I'm not complaining.
>>
>> A QAPIError looks like this when converted to str:
>>
>>     In file included from ...:
>>     ...
>>     In file included from ...:
>>     FILE: In ENTITY-TYPE 'NAME':
>>     FILE:LINE: MESSAGE
>>
>> "In file" lines appear when the error is in a sub-module.
>>
>> An "In ENTITY-TYPE" line appears when the error is in an entity
>> definition.
>>
>> The other two ExtensionError() look like
>>
>>     FILE line LINE: syntax error: MESSAGE
>>
>> and
>>
>>     FILE line LINE: MESSAGE
>>
>> More ornate, less information.
>>
>> I figure more errors can get thrown by nested_parse_with_titles() (see
>> below).  How do they look like?
>
> They look like this:
>
> Warning, treated as error:
> /home/petmay01/linaro/qemu-from-laptop/qemu/docs/../qapi/block.json:15:Bullet
> list ends without a blank line; unexpected unindent.
>
> (I've just noticed that with Sphinx 1.6, which we still have
> to support, the file/line info isn't passed through, so you get:
>
> Warning, treated as error:
> /home/petmay01/linaro/qemu-from-laptop/qemu/docs/interop/qemu-qmp-ref.rst:7:Bullet
> list ends without a blank line; unexpected unindent.
>
> The plugin has code borrowed from kerneldoc.py which is
> *supposed* to handle the older API Sphinx 1.6 used, but it
> looks like it's broken. I'll have a look and see if it
> is fixable, but possibly we may have to live with people
> developing on old distros getting suboptimal errors.)

I'm okay with that.

>> Can we avoid the information loss?
>>
>> Can we make the error message format more consistent?
>
> How do I get a QAPIError from within one of the
> visit_* or freeform methods of a QAPISchemaVisitor ?

The visit_ methods take an info argument.  With that:

    raise QAPISemError(info, "the error message")

QAPISchemaGenDocVisitor.freeform() lacks such an argument.  Use
doc.info.

> The current texinfo doc generator doesn't do anything like that.

None of the code generators detects any errors.

I added one to test the advice I gave above.  It doesn't get caught.
Easy enough to fix: move then gen_FOO() into main()'s try ... except.

> If there's a way to get a QAPIError given
>  * the 'doc' argument passed into freeform/visit*
>  * the specific error message we want to emit
> then we can make the errors this script itself emits
> (which are just the ones about headings: wrongly-subnested
> headings, and headings that aren't the only thing in their
> freeform-doc block) consistent with the other QAPIError ones.
> [As I mentioned in some earlier thread, we are identifying
> these errors here for pragmatic "it was easy" reasons,
> though you could make a case that scripts/qapi/parser.py
> should be doing it.]

Oh yes.

> (Aside: what was your conclusion on the question of section
> headers, anyway? I need to go back and re-read the older
> threads but I think that is still an unresolved topic...)

If I remember correctly, I disliked "[PATCH 19/29]
qapi/qapi-schema.json: Put headers in their own doc-comment blocks",
because "it sweeps the actual problem under the rug, namely flaws in our
parsing and representation of doc comments."

Our documentation has a tree structure.  Ideally, we'd represent it as a
tree.  I figure such a tree would suit you well: you could translate
more or less 1:1 to Sphinx nodes.

We currently have a two-level list instead: a list of blocks (either
definition or free-form), where each block has a list of sections (see
QAPIDoc's doc string).

My commit dcdc07a97c "qapi: Make section headings start a new doc
comment block" was a minimally invasive patch to get us closer to a
(partially flattened) tree.  Kind of collects the dirt in an (hopefully
obvious) pile next to the rug.

You could finish the job and rework the representation into a tree.
That would be lovely.  I appreciate help with cleaning up existing QAPI
messes very much, but do not feel obliged.

You could work with the two-level list as is, relying on the fact that a
'=' can only occur right at the start of a free-form block.

You could tweak the two-level list just a bit, so that the heading
becomes properly represented, and you don't have to match free-form body
section text to find it.

Does this help you along?

> However I suspect we can't generate QAPIErrors for errors which are
> produced by Sphinx itself when it's handed invalid rST:
> the API Sphinx provides us is that we can annotate the lines
> of rST that we are assembling from the input files to indicate
> their "true" file/line number, but we can't intercept error
> messages it emits. Think of it like the C compiler -- a
> preprocessor can add #line directives to pass fixed up file/line
> info to the compiler, but it isn't in the loop when the compiler
> actually prints out errors.

Perfection is not a requirement :)

Errors from TexInfo are arguably worse than that now.  They are rare,
however.  The situation will change when people use reST markup in doc
comments.

>> I had to read mostly backwards to understand the code.
>
> Yes. This seems to be the standard way to write a Sphinx extension.
> (Compare the kerneldoc.py and the various example extensions
> in the Sphinx docs.)

Mimicking existing similar extensions makes sense.



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

* Re: [PATCH v5 09/20] docs/sphinx: Add new qapi-doc Sphinx extension
  2020-09-21 16:50     ` Peter Maydell
@ 2020-09-22 11:47       ` Markus Armbruster
  0 siblings, 0 replies; 62+ messages in thread
From: Markus Armbruster @ 2020-09-22 11:47 UTC (permalink / raw)
  To: Peter Maydell; +Cc: John Snow, QEMU Developers

Peter Maydell <peter.maydell@linaro.org> writes:

> On Fri, 4 Sep 2020 at 15:44, Markus Armbruster <armbru@redhat.com> wrote:
>>
>> $ pycodestyle docs/sphinx/qapidoc.py
>> docs/sphinx/qapidoc.py:42:1: E302 expected 2 blank lines, found 1
>> docs/sphinx/qapidoc.py:50:1: E302 expected 2 blank lines, found 1
>> docs/sphinx/qapidoc.py:74:80: E501 line too long (80 > 79 characters)
>> docs/sphinx/qapidoc.py:388:80: E501 line too long (80 > 79 characters)
>> docs/sphinx/qapidoc.py:391:80: E501 line too long (80 > 79 characters)
>> docs/sphinx/qapidoc.py:430:1: E302 expected 2 blank lines, found 1
>> docs/sphinx/qapidoc.py:489:80: E501 line too long (80 > 79 characters)
>> docs/sphinx/qapidoc.py:495:1: E302 expected 2 blank lines, found 1
>
> All fixed.
>
>> $ PYTHONPATH=scripts pylint docs/sphinx/qapidoc.py
>> ************* Module qapidoc
>> docs/sphinx/qapidoc.py:36:4: E0611: No name 'AutodocReporter' in module 'sphinx.ext.autodoc' (no-name-in-module)
>> docs/sphinx/qapidoc.py:45:10: R1708: Do not raise StopIteration in generator, use return statement instead (stop-iteration-return)
>> docs/sphinx/qapidoc.py:104:4: R0201: Method could be a function (no-self-use)
>> docs/sphinx/qapidoc.py:253:4: R0201: Method could be a function (no-self-use)
>> docs/sphinx/qapidoc.py:34:4: C0412: Imports from package sphinx are not grouped (ungrouped-imports)
>
> Not fixed: I disagree with the linter on all these.
>
> The first and fifth of these are unfixable because they are the
> result of code that is trying to adapt to multiple versions of
> Sphinx (one of which has AutodocReporter and the other of which
> does not).
>
> The second makes no sense and appears to me to be a linter
> bug, because the code doesn't (directly) raise StopIteration.
> In any case the function being complained about is just a
> straight borrowing from pydash.
>
> The third and fourth would mean that two of the 10 or so
> _nodes_for_whatever methods would become functions and
> gratuitously different in call signature from the rest
> just because it happens that the current implementation
> doesn't need 'self'.

As I said, use your judgement.

> (The version of pylint I have also warns about:
>  * comments that say "TODO", both of which are ones that
>    are carried over from the texinfo generator about dropping
>    fallback code when undocumented members are outlawed
>  * methods that are part of the QAPISchemaVisitor
>    interface and apparently have "too many arguments")
>  * single-letter variables
>  * the Use_SSI variable name which is from the kerneldoc plugin
> I'm going to ignore those too.)

John Snow is working on a combination of pylint configuration that suits
us, genuine removal of lint, and the occasional pylint directive to shut
it up when it's wrong.  Informational, don't worry about it now.



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

* Re: [PATCH v5 08/20] scripts/qapi/parser.py: improve doc comment indent handling
  2020-09-22  7:27       ` Markus Armbruster
@ 2020-09-22 11:48         ` Peter Maydell
  2020-09-22 14:08           ` Markus Armbruster
  0 siblings, 1 reply; 62+ messages in thread
From: Peter Maydell @ 2020-09-22 11:48 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: QEMU Developers

On Tue, 22 Sep 2020 at 08:27, Markus Armbruster <armbru@redhat.com> wrote:
> How does
>
>   @foo:  bar
>          baz
>   @frob: gnu
>          gnat
>
> behave?

The rST fragments would be:

 bar
 baz

gnu
gnat

So you get what rST does with that. We do actually have examples
of this in the existing QAPI doc comments. It ends up treating it
as a definition list where the term is 'bar' and the
definition is 'baz' (which I don't entirely understand, I was
expecting a block-quote). That renders sufficiently close to correct
that I hadn't noticed it.

It would be a fairly small change to determine the indent level by
looking for the first non-ws character on line 1 after the colon.
Since we have a fair amount of this style in the code and it's
as you say a natural-seeming thing to write that seems the best
thing. (If you really wanted to start the option documentation with
some rST that required an initial indent, probably because you're
writing a literal-text Examples section, then you'd need to use the
"nothing after the : on line 1, rST fragment begins on line 2 in
column 0" style. Which would be the most natural way to write
that literal text anyway.)

I guess at this point I'll potentially create work for myself
by drawing your attention to the rST syntax for field lists
and option lists:
https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#field-lists
which are kind of similar to what we're doing with @foo: stuff
markup, and which handle indentation like this:

:Hello: This field has a short field name, so aligning the field
        body with the first line is feasible.

:Number-of-African-swallows-required-to-carry-a-coconut: It would
    be very difficult to align the field body with the left edge
    of the first line. It may even be preferable not to begin the
    body on the same line as the marker.

The differences to what I have implemented in this series are:
 * indent of lines 2+ is determined by the indent of line 2, not 1
 * lines 2+ must be indented, so anything that currently uses
   "no indent, start in column 0" would need indenting. (This would
   be a lot of change to our current docs text.)
 * it doesn't say in the spec, but I guess that spaces between
   the colon and start of line 1 text are not significant.

The advantage would be a bit more consistency with rST syntax
otherwise; the disadvantage is that we have a *lot* of text
that uses the "start in column 0" format, like this:

# @QCryptoBlockOptionsBase:
#
# The common options that apply to all full disk
# encryption formats

and we'd need to reindent it all. My view is that trying to
look more like rST indent isn't sufficiently useful to be
worth having to change all that.

> This is something people may actually write.

Indeed, they have :-)

thanks
-- PMM


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

* Re: [PATCH v5 10/20] docs/interop: Convert qemu-ga-ref to rST
  2020-09-21 15:30     ` Peter Maydell
@ 2020-09-22 12:00       ` Markus Armbruster
  2020-09-22 12:58         ` Peter Maydell
  0 siblings, 1 reply; 62+ messages in thread
From: Markus Armbruster @ 2020-09-22 12:00 UTC (permalink / raw)
  To: Peter Maydell; +Cc: QEMU Developers

Peter Maydell <peter.maydell@linaro.org> writes:

> On Fri, 4 Sep 2020 at 14:16, Markus Armbruster <armbru@redhat.com> wrote:
>>
>> Peter Maydell <peter.maydell@linaro.org> writes:
>>
>> > Convert qemu-ga-ref to rST format. This includes dropping
>> > the plain-text, pdf and info format outputs for this document;
>> > as with all our other Sphinx-based documentation, we provide
>> > HTML and manpage only.
>
>> > -@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
>>
>> Does the interop manual carry an equivalent copyright notice?
>
> NB: in the Texinfo versions, Texinfo does not put this copyright/license
> notice in its generated manpages, and it is in the generated HTML only
> as an HTML comment, not visible to the ordinary reader.

For what it's worth, the generated PDF has it on page 2.

> The Sphinx interop manual has the usual footer:
> https://www.qemu.org/docs/master/interop/index.html
> "©2020, The QEMU Project Developers."
>
> The system manual has an explicit "License" section:
> https://www.qemu.org/docs/master/system/license.html
> but that's documenting the license of the program, not the
> manual (it's docs/system/license.rst).
>
> We could do any or all of:
>  * decide that we're happy with the current situation
>  * expand the "copyright" footer to something like
>    '©2020, The QEMU Project Developers; this manual is GPLv2'
>  * have a docs/foo/license.rst for each manual, and expand
>    it to mention the documentation license as well as the
>    code license
>
> Given that the Texinfo generated QMP/QGA references don't
> actually present this text to the reader, my inclination
> is to say that this is something we should address in
> a separate patchseries, not as part of this conversion.

I think the manual should have a proper copyright notice.

If you'd prefer to (re-)add it later, stick in TODO comment.



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

* Re: [PATCH v5 10/20] docs/interop: Convert qemu-ga-ref to rST
  2020-09-22 12:00       ` Markus Armbruster
@ 2020-09-22 12:58         ` Peter Maydell
  2020-09-22 14:13           ` Markus Armbruster
  0 siblings, 1 reply; 62+ messages in thread
From: Peter Maydell @ 2020-09-22 12:58 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: QEMU Developers

On Tue, 22 Sep 2020 at 13:01, Markus Armbruster <armbru@redhat.com> wrote:
>
> Peter Maydell <peter.maydell@linaro.org> writes:
> > The Sphinx interop manual has the usual footer:
> > https://www.qemu.org/docs/master/interop/index.html
> > "©2020, The QEMU Project Developers."
> >
> > The system manual has an explicit "License" section:
> > https://www.qemu.org/docs/master/system/license.html
> > but that's documenting the license of the program, not the
> > manual (it's docs/system/license.rst).
> >
> > We could do any or all of:
> >  * decide that we're happy with the current situation
> >  * expand the "copyright" footer to something like
> >    '©2020, The QEMU Project Developers; this manual is GPLv2'
> >  * have a docs/foo/license.rst for each manual, and expand
> >    it to mention the documentation license as well as the
> >    code license
> >
> > Given that the Texinfo generated QMP/QGA references don't
> > actually present this text to the reader, my inclination
> > is to say that this is something we should address in
> > a separate patchseries, not as part of this conversion.
>
> I think the manual should have a proper copyright notice.

It does -- that's the standard Sphinx footer that reads
"©2020, The QEMU Project Developers." What it's missing
is more precise licensing information.

> If you'd prefer to (re-)add it later, stick in TODO comment.

Where would you want a TODO to be ?

thanks
-- PMM


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

* Re: [PATCH v5 08/20] scripts/qapi/parser.py: improve doc comment indent handling
  2020-09-22 11:48         ` Peter Maydell
@ 2020-09-22 14:08           ` Markus Armbruster
  0 siblings, 0 replies; 62+ messages in thread
From: Markus Armbruster @ 2020-09-22 14:08 UTC (permalink / raw)
  To: Peter Maydell; +Cc: QEMU Developers

Peter Maydell <peter.maydell@linaro.org> writes:

> On Tue, 22 Sep 2020 at 08:27, Markus Armbruster <armbru@redhat.com> wrote:
>> How does
>>
>>   @foo:  bar
>>          baz
>>   @frob: gnu
>>          gnat
>>
>> behave?
>
> The rST fragments would be:
>
>  bar
>  baz
>
> gnu
> gnat
>
> So you get what rST does with that. We do actually have examples
> of this in the existing QAPI doc comments. It ends up treating it
> as a definition list where the term is 'bar' and the
> definition is 'baz' (which I don't entirely understand, I was

The Perl of ASCII-based markups...

> expecting a block-quote). That renders sufficiently close to correct
> that I hadn't noticed it.

Understandable :)

> It would be a fairly small change to determine the indent level by
> looking for the first non-ws character on line 1 after the colon.
> Since we have a fair amount of this style in the code and it's
> as you say a natural-seeming thing to write that seems the best
> thing. (If you really wanted to start the option documentation with
> some rST that required an initial indent, probably because you're
> writing a literal-text Examples section, then you'd need to use the
> "nothing after the : on line 1, rST fragment begins on line 2 in
> column 0" style. Which would be the most natural way to write
> that literal text anyway.)

Agree.

> I guess at this point I'll potentially create work for myself
> by drawing your attention to the rST syntax for field lists
> and option lists:
> https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#field-lists
> which are kind of similar to what we're doing with @foo: stuff
> markup, and which handle indentation like this:
>
> :Hello: This field has a short field name, so aligning the field
>         body with the first line is feasible.
>
> :Number-of-African-swallows-required-to-carry-a-coconut: It would
>     be very difficult to align the field body with the left edge
>     of the first line. It may even be preferable not to begin the
>     body on the same line as the marker.
>
> The differences to what I have implemented in this series are:
>  * indent of lines 2+ is determined by the indent of line 2, not 1
>  * lines 2+ must be indented, so anything that currently uses
>    "no indent, start in column 0" would need indenting. (This would
>    be a lot of change to our current docs text.)
>  * it doesn't say in the spec, but I guess that spaces between
>    the colon and start of line 1 text are not significant.
>
> The advantage would be a bit more consistency with rST syntax
> otherwise; the disadvantage is that we have a *lot* of text
> that uses the "start in column 0" format, like this:
>
> # @QCryptoBlockOptionsBase:
> #
> # The common options that apply to all full disk
> # encryption formats
>
> and we'd need to reindent it all. My view is that trying to
> look more like rST indent isn't sufficiently useful to be
> worth having to change all that.

We use @FOO: for two distinct things:

1. Right at the beginning of a comment block, it makes the comment block
a definition doc block for symbol FOO.

2. At the beginning of an argument section, it names the argument /
member being documented.

Example:

    ##
    # @QCryptoBlockOptionsBase:                         <-- 1.
    #
    # The common options that apply to all full disk
    # encryption formats
    #
    # @format: the encryption format                    <-- 2.
    #
    # Since: 2.6
    ##

We could switch just 2. to reST field list syntax, and either keep 1. as
is, or switch it to some other reST markup that works for us.

But even if we want this, we should do it on top, to avoid complicating
and delaying this series.

>> This is something people may actually write.
>
> Indeed, they have :-)
>
> thanks
> -- PMM



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

* Re: [PATCH v5 10/20] docs/interop: Convert qemu-ga-ref to rST
  2020-09-22 12:58         ` Peter Maydell
@ 2020-09-22 14:13           ` Markus Armbruster
  2020-09-22 14:21             ` Peter Maydell
  0 siblings, 1 reply; 62+ messages in thread
From: Markus Armbruster @ 2020-09-22 14:13 UTC (permalink / raw)
  To: Peter Maydell; +Cc: QEMU Developers

Peter Maydell <peter.maydell@linaro.org> writes:

> On Tue, 22 Sep 2020 at 13:01, Markus Armbruster <armbru@redhat.com> wrote:
>>
>> Peter Maydell <peter.maydell@linaro.org> writes:
>> > The Sphinx interop manual has the usual footer:
>> > https://www.qemu.org/docs/master/interop/index.html
>> > "©2020, The QEMU Project Developers."
>> >
>> > The system manual has an explicit "License" section:
>> > https://www.qemu.org/docs/master/system/license.html
>> > but that's documenting the license of the program, not the
>> > manual (it's docs/system/license.rst).
>> >
>> > We could do any or all of:
>> >  * decide that we're happy with the current situation
>> >  * expand the "copyright" footer to something like
>> >    '©2020, The QEMU Project Developers; this manual is GPLv2'
>> >  * have a docs/foo/license.rst for each manual, and expand
>> >    it to mention the documentation license as well as the
>> >    code license
>> >
>> > Given that the Texinfo generated QMP/QGA references don't
>> > actually present this text to the reader, my inclination
>> > is to say that this is something we should address in
>> > a separate patchseries, not as part of this conversion.
>>
>> I think the manual should have a proper copyright notice.
>
> It does -- that's the standard Sphinx footer that reads
> "©2020, The QEMU Project Developers." What it's missing
> is more precise licensing information.

Yes.

>> If you'd prefer to (re-)add it later, stick in TODO comment.
>
> Where would you want a TODO to be ?

Before the patch, the licensing information is in
docs/interop/qemu-ga-ref.texi.  That file gets replaced by
docs/interop/qemu-ga-ref.rst, losing the licensing information.  What
about putting the TODO right there?



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

* Re: [PATCH v5 10/20] docs/interop: Convert qemu-ga-ref to rST
  2020-09-22 14:13           ` Markus Armbruster
@ 2020-09-22 14:21             ` Peter Maydell
  2020-09-22 14:42               ` Markus Armbruster
  0 siblings, 1 reply; 62+ messages in thread
From: Peter Maydell @ 2020-09-22 14:21 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: QEMU Developers

On Tue, 22 Sep 2020 at 15:13, Markus Armbruster <armbru@redhat.com> wrote:
>
> Peter Maydell <peter.maydell@linaro.org> writes:
>
> > On Tue, 22 Sep 2020 at 13:01, Markus Armbruster <armbru@redhat.com> wrote:
> >> If you'd prefer to (re-)add it later, stick in TODO comment.
> >
> > Where would you want a TODO to be ?
>
> Before the patch, the licensing information is in
> docs/interop/qemu-ga-ref.texi.  That file gets replaced by
> docs/interop/qemu-ga-ref.rst, losing the licensing information.  What
> about putting the TODO right there?

I can if you insist, but what makes that .rst file any
different from all the other .rst files we have in docs/
which go into our various manuals and don't have explicit
licensing comments or reader-visible text?

If we want to improve how we inform readers of the docs
license, we might:
 * add a license note to the existing copyright footer line
 * or to have every page to have a larger copyright/license
   text section at the bottom
 * or to have the interop and other manuals have a license.rst
   like the system manual does currently,
and none of those changes would require editing qemu-ga-ref.rst
or qemu-qmp-ref.rst, because the overall problem being addressed
isn't specific to those parts of the documentation, it's a
wider one that you'd fix in a different place.

thanks
-- PMM


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

* Re: [PATCH v5 10/20] docs/interop: Convert qemu-ga-ref to rST
  2020-09-22 14:21             ` Peter Maydell
@ 2020-09-22 14:42               ` Markus Armbruster
  0 siblings, 0 replies; 62+ messages in thread
From: Markus Armbruster @ 2020-09-22 14:42 UTC (permalink / raw)
  To: Peter Maydell; +Cc: QEMU Developers

Peter Maydell <peter.maydell@linaro.org> writes:

> On Tue, 22 Sep 2020 at 15:13, Markus Armbruster <armbru@redhat.com> wrote:
>>
>> Peter Maydell <peter.maydell@linaro.org> writes:
>>
>> > On Tue, 22 Sep 2020 at 13:01, Markus Armbruster <armbru@redhat.com> wrote:
>> >> If you'd prefer to (re-)add it later, stick in TODO comment.
>> >
>> > Where would you want a TODO to be ?
>>
>> Before the patch, the licensing information is in
>> docs/interop/qemu-ga-ref.texi.  That file gets replaced by
>> docs/interop/qemu-ga-ref.rst, losing the licensing information.  What
>> about putting the TODO right there?
>
> I can if you insist, but what makes that .rst file any
> different from all the other .rst files we have in docs/
> which go into our various manuals and don't have explicit
> licensing comments or reader-visible text?
>
> If we want to improve how we inform readers of the docs
> license, we might:
>  * add a license note to the existing copyright footer line
>  * or to have every page to have a larger copyright/license
>    text section at the bottom
>  * or to have the interop and other manuals have a license.rst
>    like the system manual does currently,
> and none of those changes would require editing qemu-ga-ref.rst
> or qemu-qmp-ref.rst, because the overall problem being addressed
> isn't specific to those parts of the documentation, it's a
> wider one that you'd fix in a different place.

My first preference is keeping the licensing information.  How to keep
it is up to you.

If you'd prefer not to in this series, then all I ask for is a reminder
*somewhere* that we've lost licensing information, and should put it
back.  You choose where to put it and how to phrase it.  But since you
asked, I made a suggestion.  Feel free to choose anther place or
phrasing.



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

* Re: [PATCH v5 08/20] scripts/qapi/parser.py: improve doc comment indent handling
  2020-09-04  9:03   ` Markus Armbruster
  2020-09-21 15:06     ` Peter Maydell
@ 2020-09-22 15:28     ` Peter Maydell
  1 sibling, 0 replies; 62+ messages in thread
From: Peter Maydell @ 2020-09-22 15:28 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: QEMU Developers

On Fri, 4 Sep 2020 at 10:03, Markus Armbruster <armbru@redhat.com> wrote:
> Peter Maydell <peter.maydell@linaro.org> writes:
> > Make the handling of indentation in doc comments more sophisticated,
> > so that when we see a section like:
> >
> > Notes: some text
> >        some more text
> >           indented line 3
> >
> > we save it for the doc-comment processing code as:
> >
> > some text
> > some more text
> >    indented line 3
> >
> > and when we see a section with the heading on its own line:
> >
> > Notes:
> >
> > some text
> > some more text
> >    indented text
> >
> > we also accept that and save it in the same form.
> >
> > The exception is that we always retain indentation as-is for Examples
> > sections, because these are literal text.
>
> Does docs/devel/qapi-code-gen.txt need an update?  Hmm, looks like you
> leave it to [PATCH 15] docs/devel/qapi-code-gen.txt: Update to new rST
> backend conventions.  Acceptable.  Mentioning it in the commit message
> now may make sense.

I've decided to pull the bits of patch 15 which document the
new indent rules into this patch, in the optimistic hope that
if the patchseries is OK up to this point but needs another
review round for subsequent parts we might be able to commit
this patch to master and stop further inadvertent breaches
of the new indent rules being committed. (Another couple have
already appeared in master since v5, so v6 will again start
with a patch fixing them up.)

thanks
-- PMM


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

* Re: [PATCH v5 09/20] docs/sphinx: Add new qapi-doc Sphinx extension
  2020-09-22 11:42       ` Markus Armbruster
@ 2020-09-24 13:25         ` Peter Maydell
  0 siblings, 0 replies; 62+ messages in thread
From: Peter Maydell @ 2020-09-24 13:25 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: QEMU Developers

On Tue, 22 Sep 2020 at 12:42, Markus Armbruster <armbru@redhat.com> wrote:
>
> Peter Maydell <peter.maydell@linaro.org> writes:
>
> > On Fri, 4 Sep 2020 at 13:29, Markus Armbruster <armbru@redhat.com> wrote:
> >> Got a pointer to "Writing Sphinx Extensions for Dummies" or similar?
> >
> > The upstream documentation on extending Sphinx is probably
> > a good place to start; in particular the tutorials are useful:
> >
> > https://www.sphinx-doc.org/en/master/development/index.html
>
> Do you think having this link in a comment could help future readers /
> maintainers?

Easy enough to add it, and it doesn't hurt.

> > OK, how about:
> >
> > # The purpose of this extension is to read the documentation comments
> > # in QAPI schema files, and insert them all into the current document.
> > #
> > # It implements one new rST directive, "qapi-doc::".
> > # Each qapi-doc:: directive takes one argument, which is the
> > # path of the .json file to process, relative to the source tree.
> > #
> > # The docs/conf.py file must set the qapidoc_srctree config value to
> > # the root of the QEMU source tree.
> >
> > ?
>
> Works for me with "pathname" instead of "path".  I'd also write "schema
> file" instead of ".json file", but that's a matter of taste and up to
> you.

Fixed.

> The doc string is confusing (I figure you borrowed the it along with the
> code).  str.join() works just fine for arrays.  It's not the kind of
> iterable that's the problem, it's the kind of thing the iterable yields:
> str.join() wants str, we have. docutils.node.literal.
>
> The lazy solution is to delete the confusing doc string.
>
> Here's my attempt at a non-lazy solution:
>
>     """Yield the members of *iterable* interspersed with *separator*."""

Docstring updated to that text.

> >> I think I'd rather have a separate _node_for_ifcond().  Not a demand.
> >
> > I'm not sure what you have in mind here, so I'll leave it as it is :-)
>
> _nodes_for_sections() is used like this:
>
>         self._add_doc(WHAT,
>                       [self._nodes_for_WHAT(doc, ...),
>                        self._nodes_for_features(doc),
>                        self._nodes_for_sections(doc, ifcond)])
>
> ._add_doc()'s second argument is a list of nodes for sections.
>
> ._nodes_for_WHAT() computes the nodes specific to the kind of thing
> we're processing: enum, object, alternate, command, event.
>
> ._nodes_for_features() computes the nodes for the feature section.
>
> ._nodes_for_sections() computes both the nodes for additional sections
> and the nodes for the ifcond section.
>
> I'd compute the nodes for the ifcond section in their own function, to
> keep .nodes_for_sections() focused on just one purpose.

Oh, OK, that makes sense. The name "_nodes_for_ifcond" is already
taken, though, so I'll call it "_nodes_for_if_section".

> >> Makes me wonder why you build two levels in the first place.
> >
> > This is probably just me not being a very experienced Python
> > programmer. What's the syntax for passing _add_doc the single list
> > which is just the concatenation of the various lists that
> > the _nodes_for_* methods return ?
>
> You could replace
>
>                       [self._nodes_for_WHAT(doc, ...),
>                        self._nodes_for_features(doc),
>                        self._nodes_for_sections(doc, ifcond)])
>
> by
>
>                       self._nodes_for_WHAT(doc, ...)
>                       + self._nodes_for_features(doc)
>                       + self._nodes_for_sections(doc, ifcond)
>
> Operator + concatenates sequences.

Right. We also need to change some of the _nodes_for_whatever
functions that were sometimes returning just a single node
(eg "return section") to instead return a one-element list
("return [section]"), and make them return [] where they were
previously returning None. But this seems like a good change,
as then all these functions consistently return a list
(which might have 0, 1 or many elements).

> > How do I get a QAPIError from within one of the
> > visit_* or freeform methods of a QAPISchemaVisitor ?
>
> The visit_ methods take an info argument.  With that:
>
>     raise QAPISemError(info, "the error message")

We need to launder it into an ExtensionError, so
   qapierr = QAPISemError(info, "message")
   raise ExtensionError(str(qapierr))

but otherwise this works nicely, and you get:

Extension error:
In file included from
/home/petmay01/linaro/qemu-from-laptop/qemu/docs/../qapi/qapi-schema.json:72:
/home/petmay01/linaro/qemu-from-laptop/qemu/docs/../qapi/block.json:4:
section or subsection heading must be in its own doc comment block

(Or we could do the launder-QAPIError-into-ExtensionError
in run() by extending the scope of its try..except, as I think
you suggest below.)

> > (Aside: what was your conclusion on the question of section
> > headers, anyway? I need to go back and re-read the older
> > threads but I think that is still an unresolved topic...)
>
> If I remember correctly, I disliked "[PATCH 19/29]
> qapi/qapi-schema.json: Put headers in their own doc-comment blocks",
> because "it sweeps the actual problem under the rug, namely flaws in our
> parsing and representation of doc comments."
>
> Our documentation has a tree structure.  Ideally, we'd represent it as a
> tree.  I figure such a tree would suit you well: you could translate
> more or less 1:1 to Sphinx nodes.
>
> We currently have a two-level list instead: a list of blocks (either
> definition or free-form), where each block has a list of sections (see
> QAPIDoc's doc string).
>
> My commit dcdc07a97c "qapi: Make section headings start a new doc
> comment block" was a minimally invasive patch to get us closer to a
> (partially flattened) tree.  Kind of collects the dirt in an (hopefully
> obvious) pile next to the rug.

Ah, yes, I'd forgotten that commit. So I can drop the bit of
code you disliked previously that insists on section headings
being in their own freeform block (handling "header line followed
by freeform text" is easy). That also incidentally means we're down
to only one parser-error in the qapidoc.py, which is the one that
catches mis-ordered section headings (eg using '=== header' when
you hadn't previously used '== header').

> You could finish the job and rework the representation into a tree.
> That would be lovely.  I appreciate help with cleaning up existing QAPI
> messes very much, but do not feel obliged.
>
> You could work with the two-level list as is, relying on the fact that a
> '=' can only occur right at the start of a free-form block.

I'll take this option, I think.

> You could tweak the two-level list just a bit, so that the heading
> becomes properly represented, and you don't have to match free-form body
> section text to find it.

thanks
-- PMM


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

* Re: [PATCH v5 00/20] Convert QAPI doc comments to generate rST instead of texinfo
  2020-09-04 16:05         ` Peter Maydell
@ 2020-09-24 14:13           ` Peter Maydell
  2020-09-24 14:49             ` Markus Armbruster
  0 siblings, 1 reply; 62+ messages in thread
From: Peter Maydell @ 2020-09-24 14:13 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: QEMU Developers

On Fri, 4 Sep 2020 at 17:05, Peter Maydell <peter.maydell@linaro.org> wrote:
> On Fri, 4 Sep 2020 at 16:54, Markus Armbruster <armbru@redhat.com> wrote:
> > visit_module() lets you see the modules.
> >
> > visit_include() lets you see the includes.  The same module can be
> > included multiple times.  Having to filter that out would be annoying.
>
> I don't think you'd need to filter it out for this use case -- I
> assume Sphinx would just ignore the unnecessary extra dependency
> information. But if visit_module() does what we want and you
> recommend it I'll look at that.

visit_module() seems to work. I notice it gets called with None
before it's called with the filename of the top level qapi-schema.json,
but it's easy enough to ignore the 'None' call...

thanks
-- PMM


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

* Re: [PATCH v5 00/20] Convert QAPI doc comments to generate rST instead of texinfo
  2020-09-24 14:13           ` Peter Maydell
@ 2020-09-24 14:49             ` Markus Armbruster
  0 siblings, 0 replies; 62+ messages in thread
From: Markus Armbruster @ 2020-09-24 14:49 UTC (permalink / raw)
  To: Peter Maydell; +Cc: QEMU Developers

Peter Maydell <peter.maydell@linaro.org> writes:

> On Fri, 4 Sep 2020 at 17:05, Peter Maydell <peter.maydell@linaro.org> wrote:
>> On Fri, 4 Sep 2020 at 16:54, Markus Armbruster <armbru@redhat.com> wrote:
>> > visit_module() lets you see the modules.
>> >
>> > visit_include() lets you see the includes.  The same module can be
>> > included multiple times.  Having to filter that out would be annoying.
>>
>> I don't think you'd need to filter it out for this use case -- I
>> assume Sphinx would just ignore the unnecessary extra dependency
>> information. But if visit_module() does what we want and you
>> recommend it I'll look at that.
>
> visit_module() seems to work. I notice it gets called with None
> before it's called with the filename of the top level qapi-schema.json,
> but it's easy enough to ignore the 'None' call...

None stands for the (nameless) module containing built-in stuff.



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

* Re: [PATCH v5 09/20] docs/sphinx: Add new qapi-doc Sphinx extension
  2020-09-21 18:06     ` Peter Maydell
  2020-09-22 11:42       ` Markus Armbruster
@ 2020-09-24 16:30       ` Peter Maydell
  2020-09-25  5:51         ` Markus Armbruster
  1 sibling, 1 reply; 62+ messages in thread
From: Peter Maydell @ 2020-09-24 16:30 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: QEMU Developers

On Mon, 21 Sep 2020 at 19:06, Peter Maydell <peter.maydell@linaro.org> wrote:
> (I've just noticed that with Sphinx 1.6, which we still have
> to support, the file/line info isn't passed through, so you get:
>
> Warning, treated as error:
> /home/petmay01/linaro/qemu-from-laptop/qemu/docs/interop/qemu-qmp-ref.rst:7:Bullet
> list ends without a blank line; unexpected unindent.
>
> The plugin has code borrowed from kerneldoc.py which is
> *supposed* to handle the older API Sphinx 1.6 used, but it
> looks like it's broken. I'll have a look and see if it
> is fixable, but possibly we may have to live with people
> developing on old distros getting suboptimal errors.)

Tracked down the cause of this -- it turns out that if you
feed nested_parse_with_titles() bogus rST then in some
cases it will detect the error with a line number that's
one off the end of the input text, eg on the 2 lines:
0: * a bulleted list
1: a misindented line

there's a syntax error here where line 1 is misindented,
but at least Sphinx 1.6 wants to attribute the error to a
nonexistent line 2, which then doesn't match in the
input-lines-to-source-info mapping for the fragment
and so gets reported for the next level out (the .rst file).
It just happened that the syntax error I used to test the
file/line reporting this time around was one of this kind.
I assume Sphinx 3 either gets the line attribution more
accurate or is not using the same mechanism for finding
the input line in the source mapping.

The fix is just to always add a blank line at the end of
every .rst fragment we hand to the Sphinx parser, which
doesn't affect the generated output and does sidestep this
issue.

thanks
-- PMM


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

* Re: [PATCH v5 14/20] scripts/qapi: Remove texinfo generation support
  2020-09-04 13:37   ` Markus Armbruster
@ 2020-09-24 18:14     ` Peter Maydell
  2020-09-25  6:48       ` Markus Armbruster
  0 siblings, 1 reply; 62+ messages in thread
From: Peter Maydell @ 2020-09-24 18:14 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: QEMU Developers

On Fri, 4 Sep 2020 at 14:37, Markus Armbruster <armbru@redhat.com> wrote:
> Peter Maydell <peter.maydell@linaro.org> writes:
> > -.PHONY: check-tests/qapi-schema/doc-good.texi
> > -check-tests/qapi-schema/doc-good.texi: tests/qapi-schema/doc-good.test.texi
> > -     @diff -u $(SRC_PATH)/tests/qapi-schema/doc-good.texi $<

> We shouldn't just delete this test.
>
> It is for checking the doc generator does what it should for "good"
> input.  "Bad" input is coverd by the other doc-*.json.
>
> With the old doc generation system, the testing "good" input is
> straightforward: generate Texinfo, diff to expected Texinfo, which is
> committed to git.
>
> This test has been invaliable when maintaining and extending doc.py.
>
> With the new system, there is no ouput suitable for diffing, as the
> various outputs all depend on the version of Sphinx.
>
> Or is there?  Is there a way to have Sphinx "unparse" its internal
> representation of the input?

There is no built-in "unparse the internal representation" option.
We could add one as a Sphinx extension (basically defining a new
output format that was "print what you get"). This too is at
least potentially liable to breakage with future versions, both
if the Sphinx APIs for output-format extensions and change and
if core Sphinx gets changes that mean input rST is parsed into
a different-but-equivalent internal-tree-of-nodes representation.

The HTML output definitely depends on the Sphinx version.
The Texinfo output doesn't differ much, but it does differ in
a couple of places (firstly it has the Sphinx version number
baked into, and secondly what looks like a null-effect change
in ordoring of @anchor{} nodes).
The plain-text output is identical between Sphinx 1.6 and 3.0.
(I think this is mostly because nobody really cares about it
as an output generator, so it hasn't had any changes made to
it other than general whole-tree cleanup type stuff...)

So we could go for a simple comparison of the plaintext, and
hope future Sphinx versions don't break it. (If they did we'd
need to put together something like the iotests handling of
"these parts need to match and these might be anything" in
the golden-reference).

thanks
-- PMM


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

* Re: [PATCH v5 09/20] docs/sphinx: Add new qapi-doc Sphinx extension
  2020-09-24 16:30       ` Peter Maydell
@ 2020-09-25  5:51         ` Markus Armbruster
  0 siblings, 0 replies; 62+ messages in thread
From: Markus Armbruster @ 2020-09-25  5:51 UTC (permalink / raw)
  To: Peter Maydell; +Cc: QEMU Developers

Peter Maydell <peter.maydell@linaro.org> writes:

> On Mon, 21 Sep 2020 at 19:06, Peter Maydell <peter.maydell@linaro.org> wrote:
>> (I've just noticed that with Sphinx 1.6, which we still have
>> to support, the file/line info isn't passed through, so you get:
>>
>> Warning, treated as error:
>> /home/petmay01/linaro/qemu-from-laptop/qemu/docs/interop/qemu-qmp-ref.rst:7:Bullet
>> list ends without a blank line; unexpected unindent.
>>
>> The plugin has code borrowed from kerneldoc.py which is
>> *supposed* to handle the older API Sphinx 1.6 used, but it
>> looks like it's broken. I'll have a look and see if it
>> is fixable, but possibly we may have to live with people
>> developing on old distros getting suboptimal errors.)
>
> Tracked down the cause of this -- it turns out that if you
> feed nested_parse_with_titles() bogus rST then in some
> cases it will detect the error with a line number that's
> one off the end of the input text, eg on the 2 lines:
> 0: * a bulleted list
> 1: a misindented line
>
> there's a syntax error here where line 1 is misindented,
> but at least Sphinx 1.6 wants to attribute the error to a
> nonexistent line 2, which then doesn't match in the
> input-lines-to-source-info mapping for the fragment
> and so gets reported for the next level out (the .rst file).
> It just happened that the syntax error I used to test the
> file/line reporting this time around was one of this kind.

Lucky!

> I assume Sphinx 3 either gets the line attribution more
> accurate or is not using the same mechanism for finding
> the input line in the source mapping.
>
> The fix is just to always add a blank line at the end of
> every .rst fragment we hand to the Sphinx parser, which
> doesn't affect the generated output and does sidestep this
> issue.

Sounds good, as long as it has a comment explaining why we need this.



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

* Re: [PATCH v5 14/20] scripts/qapi: Remove texinfo generation support
  2020-09-24 18:14     ` Peter Maydell
@ 2020-09-25  6:48       ` Markus Armbruster
  0 siblings, 0 replies; 62+ messages in thread
From: Markus Armbruster @ 2020-09-25  6:48 UTC (permalink / raw)
  To: Peter Maydell; +Cc: QEMU Developers

Peter Maydell <peter.maydell@linaro.org> writes:

> On Fri, 4 Sep 2020 at 14:37, Markus Armbruster <armbru@redhat.com> wrote:
>> Peter Maydell <peter.maydell@linaro.org> writes:
>> > -.PHONY: check-tests/qapi-schema/doc-good.texi
>> > -check-tests/qapi-schema/doc-good.texi: tests/qapi-schema/doc-good.test.texi
>> > -     @diff -u $(SRC_PATH)/tests/qapi-schema/doc-good.texi $<
>
>> We shouldn't just delete this test.
>>
>> It is for checking the doc generator does what it should for "good"
>> input.  "Bad" input is coverd by the other doc-*.json.
>>
>> With the old doc generation system, the testing "good" input is
>> straightforward: generate Texinfo, diff to expected Texinfo, which is
>> committed to git.
>>
>> This test has been invaliable when maintaining and extending doc.py.
>>
>> With the new system, there is no ouput suitable for diffing, as the
>> various outputs all depend on the version of Sphinx.
>>
>> Or is there?  Is there a way to have Sphinx "unparse" its internal
>> representation of the input?
>
> There is no built-in "unparse the internal representation" option.
> We could add one as a Sphinx extension (basically defining a new
> output format that was "print what you get"). This too is at
> least potentially liable to breakage with future versions, both
> if the Sphinx APIs for output-format extensions and change and
> if core Sphinx gets changes that mean input rST is parsed into
> a different-but-equivalent internal-tree-of-nodes representation.

Yes.  We could update the test for current Sphinx then, and disable it
for old Sphinx.  Not ideal, but good enough, I think.

> The HTML output definitely depends on the Sphinx version.
> The Texinfo output doesn't differ much, but it does differ in
> a couple of places (firstly it has the Sphinx version number
> baked into, and secondly what looks like a null-effect change
> in ordoring of @anchor{} nodes).
> The plain-text output is identical between Sphinx 1.6 and 3.0.
> (I think this is mostly because nobody really cares about it
> as an output generator, so it hasn't had any changes made to
> it other than general whole-tree cleanup type stuff...)
>
> So we could go for a simple comparison of the plaintext, and
> hope future Sphinx versions don't break it. (If they did we'd
> need to put together something like the iotests handling of
> "these parts need to match and these might be anything" in
> the golden-reference).

Again, we could also update the test for current Sphinx then, and
disable it for old Sphinx.

Diffing plain text output is a weaker test than diffing the intermediate
Texinfo or a Sphinx unparse.  Still better than nothing.

Blocking this series on a a yet-to-be-written unparse extension would be
a bad idea.  But thinking things through is not :)



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

end of thread, other threads:[~2020-09-25  6:49 UTC | newest]

Thread overview: 62+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-08-10 19:49 [PATCH v5 00/20] Convert QAPI doc comments to generate rST instead of texinfo Peter Maydell
2020-08-10 19:50 ` [PATCH v5 01/20] qapi/migration.json: Fix indentation Peter Maydell
2020-08-10 19:50 ` [PATCH v5 02/20] qapi: Fix indentation, again Peter Maydell
2020-08-14 18:39   ` Richard Henderson
2020-08-10 19:50 ` [PATCH v5 03/20] qapi/block-core.json: Fix nbd-server-start docs Peter Maydell
2020-08-14 18:39   ` Richard Henderson
2020-08-10 19:50 ` [PATCH v5 04/20] qapi/qapi-schema.json: Put headers in their own doc-comment blocks Peter Maydell
2020-08-10 19:50 ` [PATCH v5 05/20] qapi/machine.json: Escape a literal '*' in doc comment Peter Maydell
2020-08-10 19:50 ` [PATCH v5 06/20] tests/qapi/doc-good.json: Prepare for qapi-doc Sphinx extension Peter Maydell
2020-09-04  8:10   ` Markus Armbruster
2020-09-04 12:17     ` Peter Maydell
2020-08-10 19:50 ` [PATCH v5 07/20] scripts/qapi: Move doc-comment whitespace stripping to doc.py Peter Maydell
2020-08-10 19:50 ` [PATCH v5 08/20] scripts/qapi/parser.py: improve doc comment indent handling Peter Maydell
2020-09-04  9:03   ` Markus Armbruster
2020-09-21 15:06     ` Peter Maydell
2020-09-22  7:27       ` Markus Armbruster
2020-09-22 11:48         ` Peter Maydell
2020-09-22 14:08           ` Markus Armbruster
2020-09-22 15:28     ` Peter Maydell
2020-08-10 19:50 ` [PATCH v5 09/20] docs/sphinx: Add new qapi-doc Sphinx extension Peter Maydell
2020-08-14 18:40   ` Richard Henderson
2020-09-04 12:29   ` Markus Armbruster
2020-09-21 18:06     ` Peter Maydell
2020-09-22 11:42       ` Markus Armbruster
2020-09-24 13:25         ` Peter Maydell
2020-09-24 16:30       ` Peter Maydell
2020-09-25  5:51         ` Markus Armbruster
2020-09-04 14:44   ` Markus Armbruster
2020-09-04 14:52     ` Peter Maydell
2020-09-21 16:50     ` Peter Maydell
2020-09-22 11:47       ` Markus Armbruster
2020-08-10 19:50 ` [PATCH v5 10/20] docs/interop: Convert qemu-ga-ref to rST Peter Maydell
2020-09-04 13:16   ` Markus Armbruster
2020-09-04 13:18     ` Peter Maydell
2020-09-21 15:30     ` Peter Maydell
2020-09-22 12:00       ` Markus Armbruster
2020-09-22 12:58         ` Peter Maydell
2020-09-22 14:13           ` Markus Armbruster
2020-09-22 14:21             ` Peter Maydell
2020-09-22 14:42               ` Markus Armbruster
2020-08-10 19:50 ` [PATCH v5 11/20] docs/interop: Convert qemu-qmp-ref " Peter Maydell
2020-08-10 19:50 ` [PATCH v5 12/20] qapi: Use rST markup for literal blocks Peter Maydell
2020-09-04 13:02   ` Markus Armbruster
2020-08-10 19:50 ` [PATCH v5 13/20] qga/qapi-schema.json: Add some headings Peter Maydell
2020-08-10 19:50 ` [PATCH v5 14/20] scripts/qapi: Remove texinfo generation support Peter Maydell
2020-09-04 13:37   ` Markus Armbruster
2020-09-24 18:14     ` Peter Maydell
2020-09-25  6:48       ` Markus Armbruster
2020-08-10 19:50 ` [PATCH v5 15/20] docs/devel/qapi-code-gen.txt: Update to new rST backend conventions Peter Maydell
2020-09-17  9:24   ` Markus Armbruster
2020-08-10 19:50 ` [PATCH v5 16/20] Makefile: Remove redundant Texinfo related rules Peter Maydell
2020-08-10 19:50 ` [PATCH v5 17/20] scripts/texi2pod: Delete unused script Peter Maydell
2020-08-10 19:50 ` [PATCH v5 18/20] Remove Texinfo related files from .gitignore and git.orderfile Peter Maydell
2020-08-10 19:50 ` [PATCH v5 19/20] configure: Drop texinfo requirement Peter Maydell
2020-08-10 19:50 ` [PATCH v5 20/20] Remove texinfo dependency from docker and CI configs Peter Maydell
2020-08-27 11:25 ` [PATCH v5 00/20] Convert QAPI doc comments to generate rST instead of texinfo Peter Maydell
2020-09-04 14:34   ` Markus Armbruster
2020-09-04 14:48     ` Peter Maydell
2020-09-04 15:54       ` Markus Armbruster
2020-09-04 16:05         ` Peter Maydell
2020-09-24 14:13           ` Peter Maydell
2020-09-24 14:49             ` 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.