diff mbox series

[RFC,2/3] pxtool: Add new qemu-img command info generation tool

Message ID 20190410012413.31569-3-jsnow@redhat.com (mailing list archive)
State New, archived
Headers show
Series qemu-img: remove command documentation duplication | expand

Commit Message

John Snow April 10, 2019, 1:24 a.m. UTC
Presently we use hxtool and a .hx format to generate a few things like
the qemu_img subcommand dispatch table, the qemu_img help() display output,
and a help output in qemu-img.texi.

Unfortunately, this means that this information is duplicated in at least
three places:

(1) in qemu-img-cmds.hx as a human readable string
(2) in qemu-img-cmds.hx as a texi string
(3) in qemu-img.texi again, as a texi string

We can do a little better, though: all of these sources can be generated
from a single json file. Add a new small tool ("pxtool") that can do this.

This tool can at least handle generating (1) and (2) from the same source
without needing to reduplicate that information. Deduplicating (3) happens
in the next patch.

Notes:
 - The json format was chosen to be very "stupid", and the command line
   documentation is being kept one-per-line to make future diffs easier
   to read.
 - It's easy enough to generate the human-readable output from the texi
   output by removing '@var{foo}' with 'foo'.
 - The qemu-img command dispatch always calls img_cmdname, so we don't
   need to store this information separately, either.
 - The need for DEF() macros could be removed as well, but I left it in
   to create a minimally disruptive patch.
Signed-off-by: John Snow <jsnow@redhat.com>
---
 Makefile           |   8 +--
 qemu-img-cmds.json | 165 +++++++++++++++++++++++++++++++++++++++++++++
 qemu-img-cmds.hx   | 102 ----------------------------
 scripts/pxtool.py  |  49 ++++++++++++++
 4 files changed, 218 insertions(+), 106 deletions(-)
 create mode 100644 qemu-img-cmds.json
 delete mode 100644 qemu-img-cmds.hx
 create mode 100755 scripts/pxtool.py

Comments

Markus Armbruster April 10, 2019, 5:54 a.m. UTC | #1
John Snow <jsnow@redhat.com> writes:

> Presently we use hxtool and a .hx format to generate a few things like
> the qemu_img subcommand dispatch table, the qemu_img help() display output,
> and a help output in qemu-img.texi.
>
> Unfortunately, this means that this information is duplicated in at least
> three places:
>
> (1) in qemu-img-cmds.hx as a human readable string
> (2) in qemu-img-cmds.hx as a texi string
> (3) in qemu-img.texi again, as a texi string
>
> We can do a little better, though: all of these sources can be generated
> from a single json file. Add a new small tool ("pxtool") that can do this.
>
> This tool can at least handle generating (1) and (2) from the same source
> without needing to reduplicate that information. Deduplicating (3) happens
> in the next patch.
>
> Notes:
>  - The json format was chosen to be very "stupid", and the command line
>    documentation is being kept one-per-line to make future diffs easier
>    to read.
>  - It's easy enough to generate the human-readable output from the texi
>    output by removing '@var{foo}' with 'foo'.
>  - The qemu-img command dispatch always calls img_cmdname, so we don't
>    need to store this information separately, either.
>  - The need for DEF() macros could be removed as well, but I left it in
>    to create a minimally disruptive patch.
> Signed-off-by: John Snow <jsnow@redhat.com>

We got just five .hx:

    qemu-img.cmds.hx            killed off by this patch
    qemu-options.hx             CLI QAPIfication should kill this off
    hw/audio/pl041.hx           misnamed, not actually food for hxtool
    hmp-commands.hx             no exit strategy
    hmp-commands-info.hx            for these two, yet

CLI QAPIfication got stuck in the back-burner, and as long as that's the
case, it's not an alternative to your patches.
John Snow April 10, 2019, 5:55 p.m. UTC | #2
On 4/10/19 1:54 AM, Markus Armbruster wrote:
> John Snow <jsnow@redhat.com> writes:
> 
>> Presently we use hxtool and a .hx format to generate a few things like
>> the qemu_img subcommand dispatch table, the qemu_img help() display output,
>> and a help output in qemu-img.texi.
>>
>> Unfortunately, this means that this information is duplicated in at least
>> three places:
>>
>> (1) in qemu-img-cmds.hx as a human readable string
>> (2) in qemu-img-cmds.hx as a texi string
>> (3) in qemu-img.texi again, as a texi string
>>
>> We can do a little better, though: all of these sources can be generated
>> from a single json file. Add a new small tool ("pxtool") that can do this.
>>
>> This tool can at least handle generating (1) and (2) from the same source
>> without needing to reduplicate that information. Deduplicating (3) happens
>> in the next patch.
>>
>> Notes:
>>  - The json format was chosen to be very "stupid", and the command line
>>    documentation is being kept one-per-line to make future diffs easier
>>    to read.
>>  - It's easy enough to generate the human-readable output from the texi
>>    output by removing '@var{foo}' with 'foo'.
>>  - The qemu-img command dispatch always calls img_cmdname, so we don't
>>    need to store this information separately, either.
>>  - The need for DEF() macros could be removed as well, but I left it in
>>    to create a minimally disruptive patch.
>> Signed-off-by: John Snow <jsnow@redhat.com>
> 
> We got just five .hx:
> 
>     qemu-img.cmds.hx            killed off by this patch
>     qemu-options.hx             CLI QAPIfication should kill this off
>     hw/audio/pl041.hx           misnamed, not actually food for hxtool
>     hmp-commands.hx             no exit strategy
>     hmp-commands-info.hx            for these two, yet
> 
> CLI QAPIfication got stuck in the back-burner, and as long as that's the
> case, it's not an alternative to your patches.
> 

Good to know. I figure that at least while we wait on something more
comprehensive there's no real short term harm in tidying up.

Something I'd really like to do is define a python/json-esque
configuration file that allows you to specify some "common options" that
are shared between all of the sub-commands, and then define each command
in terms of both which common options it possesses, and then any local
command-specific options it has.

(Why the common options design? So that argument names and command
options can be encouraged to be identical and identically documented
between all subcommands that use them.)

Then, from the configuration file, generate all of the necessary C
parsers (I have a protoype for this, it's not difficult to do in at
least the basic case) that can return boxed command arguments, and then
also generate the help strings from that metadata.

I suspect that work *IS* something that might brush up against / should
use (or extend) QAPI code.

Then, I'd like to find a way to split qemu-img.texi into sub-command
files and find a way to source the same "informative text" for both:

(1) The texi output, as per usual, and
(2) qemu-img subcommand --help

such that --help, when passed as an argument to the subcommand, prints
out help only relevant to the subcommand instead, e.g.

`qemu-img check --help`

might print the "common options" section only as it relates to commands
actually used by the check command, then prints the check section of the
texi as formatted for terminals.

This way, we can have manpages, html pages, and interactive help text
all derived from the same semi-automated sources, always up to date and
much easier to read/discover/parse by human eyeballs.

That's what I'd like to accomplish, ultimately.

For now, I think this RFC set is not harmful and won't bother anybody.
It's definitely not worse than what we have now, fragility of removing
@var{} tokens and all.

--js
Markus Armbruster April 11, 2019, 6:22 a.m. UTC | #3
John Snow <jsnow@redhat.com> writes:

> On 4/10/19 1:54 AM, Markus Armbruster wrote:
>> John Snow <jsnow@redhat.com> writes:
>> 
>>> Presently we use hxtool and a .hx format to generate a few things like
>>> the qemu_img subcommand dispatch table, the qemu_img help() display output,
>>> and a help output in qemu-img.texi.
>>>
>>> Unfortunately, this means that this information is duplicated in at least
>>> three places:
>>>
>>> (1) in qemu-img-cmds.hx as a human readable string
>>> (2) in qemu-img-cmds.hx as a texi string
>>> (3) in qemu-img.texi again, as a texi string
>>>
>>> We can do a little better, though: all of these sources can be generated
>>> from a single json file. Add a new small tool ("pxtool") that can do this.
>>>
>>> This tool can at least handle generating (1) and (2) from the same source
>>> without needing to reduplicate that information. Deduplicating (3) happens
>>> in the next patch.
>>>
>>> Notes:
>>>  - The json format was chosen to be very "stupid", and the command line
>>>    documentation is being kept one-per-line to make future diffs easier
>>>    to read.
>>>  - It's easy enough to generate the human-readable output from the texi
>>>    output by removing '@var{foo}' with 'foo'.
>>>  - The qemu-img command dispatch always calls img_cmdname, so we don't
>>>    need to store this information separately, either.
>>>  - The need for DEF() macros could be removed as well, but I left it in
>>>    to create a minimally disruptive patch.
>>> Signed-off-by: John Snow <jsnow@redhat.com>
>> 
>> We got just five .hx:
>> 
>>     qemu-img.cmds.hx            killed off by this patch
>>     qemu-options.hx             CLI QAPIfication should kill this off
>>     hw/audio/pl041.hx           misnamed, not actually food for hxtool
>>     hmp-commands.hx             no exit strategy
>>     hmp-commands-info.hx            for these two, yet
>> 
>> CLI QAPIfication got stuck in the back-burner, and as long as that's the
>> case, it's not an alternative to your patches.
>> 
>
> Good to know. I figure that at least while we wait on something more
> comprehensive there's no real short term harm in tidying up.
>
> Something I'd really like to do is define a python/json-esque
> configuration file that allows you to specify some "common options" that
> are shared between all of the sub-commands, and then define each command
> in terms of both which common options it possesses, and then any local
> command-specific options it has.
>
> (Why the common options design? So that argument names and command
> options can be encouraged to be identical and identically documented
> between all subcommands that use them.)

In QAPI, you'd define a common base type.

> Then, from the configuration file, generate all of the necessary C
> parsers (I have a protoype for this, it's not difficult to do in at
> least the basic case) that can return boxed command arguments, and then
> also generate the help strings from that metadata.

With QAPI, we generate reference documentation from the doc comments.

My RFC patches for command line QAPIfication generate a CLI parser using
getopt_long_only().

They keep help texts in the QAPI schema.  Generating their formal part
from the type definition should be feasible.  Generating it without
regressing readability might turn out to be non-trivial.

My patches generate *one* CLI parser.  We have many, and some of them
want to share types.  There's more than one way to skin this cat, but
it'll have to be skinned.

> I suspect that work *IS* something that might brush up against / should
> use (or extend) QAPI code.

Seems likely.

> Then, I'd like to find a way to split qemu-img.texi into sub-command
> files and find a way to source the same "informative text" for both:
>
> (1) The texi output, as per usual, and
> (2) qemu-img subcommand --help
>
> such that --help, when passed as an argument to the subcommand, prints
> out help only relevant to the subcommand instead, e.g.
>
> `qemu-img check --help`
>
> might print the "common options" section only as it relates to commands
> actually used by the check command, then prints the check section of the
> texi as formatted for terminals.
>
> This way, we can have manpages, html pages, and interactive help text
> all derived from the same semi-automated sources, always up to date and
> much easier to read/discover/parse by human eyeballs.
>
> That's what I'd like to accomplish, ultimately.
>
> For now, I think this RFC set is not harmful and won't bother anybody.
> It's definitely not worse than what we have now, fragility of removing
> @var{} tokens and all.

Makes sense.  I just hope we can avoid duplicating work.
John Snow April 11, 2019, 8:02 p.m. UTC | #4
On 4/11/19 2:22 AM, Markus Armbruster wrote:
> John Snow <jsnow@redhat.com> writes:
> 
>> On 4/10/19 1:54 AM, Markus Armbruster wrote:
>>> John Snow <jsnow@redhat.com> writes:
>>>
>>>> Presently we use hxtool and a .hx format to generate a few things like
>>>> the qemu_img subcommand dispatch table, the qemu_img help() display output,
>>>> and a help output in qemu-img.texi.
>>>>
>>>> Unfortunately, this means that this information is duplicated in at least
>>>> three places:
>>>>
>>>> (1) in qemu-img-cmds.hx as a human readable string
>>>> (2) in qemu-img-cmds.hx as a texi string
>>>> (3) in qemu-img.texi again, as a texi string
>>>>
>>>> We can do a little better, though: all of these sources can be generated
>>>> from a single json file. Add a new small tool ("pxtool") that can do this.
>>>>
>>>> This tool can at least handle generating (1) and (2) from the same source
>>>> without needing to reduplicate that information. Deduplicating (3) happens
>>>> in the next patch.
>>>>
>>>> Notes:
>>>>  - The json format was chosen to be very "stupid", and the command line
>>>>    documentation is being kept one-per-line to make future diffs easier
>>>>    to read.
>>>>  - It's easy enough to generate the human-readable output from the texi
>>>>    output by removing '@var{foo}' with 'foo'.
>>>>  - The qemu-img command dispatch always calls img_cmdname, so we don't
>>>>    need to store this information separately, either.
>>>>  - The need for DEF() macros could be removed as well, but I left it in
>>>>    to create a minimally disruptive patch.
>>>> Signed-off-by: John Snow <jsnow@redhat.com>
>>>
>>> We got just five .hx:
>>>
>>>     qemu-img.cmds.hx            killed off by this patch
>>>     qemu-options.hx             CLI QAPIfication should kill this off
>>>     hw/audio/pl041.hx           misnamed, not actually food for hxtool
>>>     hmp-commands.hx             no exit strategy
>>>     hmp-commands-info.hx            for these two, yet
>>>
>>> CLI QAPIfication got stuck in the back-burner, and as long as that's the
>>> case, it's not an alternative to your patches.
>>>
>>
>> Good to know. I figure that at least while we wait on something more
>> comprehensive there's no real short term harm in tidying up.
>>
>> Something I'd really like to do is define a python/json-esque
>> configuration file that allows you to specify some "common options" that
>> are shared between all of the sub-commands, and then define each command
>> in terms of both which common options it possesses, and then any local
>> command-specific options it has.
>>
>> (Why the common options design? So that argument names and command
>> options can be encouraged to be identical and identically documented
>> between all subcommands that use them.)
> 
> In QAPI, you'd define a common base type.
> 
>> Then, from the configuration file, generate all of the necessary C
>> parsers (I have a protoype for this, it's not difficult to do in at
>> least the basic case) that can return boxed command arguments, and then
>> also generate the help strings from that metadata.
> 
> With QAPI, we generate reference documentation from the doc comments.
> 
> My RFC patches for command line QAPIfication generate a CLI parser using
> getopt_long_only().
> 
> They keep help texts in the QAPI schema.  Generating their formal part
> from the type definition should be feasible.  Generating it without
> regressing readability might turn out to be non-trivial.
> 
> My patches generate *one* CLI parser.  We have many, and some of them
> want to share types.  There's more than one way to skin this cat, but
> it'll have to be skinned.
> 
>> I suspect that work *IS* something that might brush up against / should
>> use (or extend) QAPI code.
> 
> Seems likely.
> 
>> Then, I'd like to find a way to split qemu-img.texi into sub-command
>> files and find a way to source the same "informative text" for both:
>>
>> (1) The texi output, as per usual, and
>> (2) qemu-img subcommand --help
>>
>> such that --help, when passed as an argument to the subcommand, prints
>> out help only relevant to the subcommand instead, e.g.
>>
>> `qemu-img check --help`
>>
>> might print the "common options" section only as it relates to commands
>> actually used by the check command, then prints the check section of the
>> texi as formatted for terminals.
>>
>> This way, we can have manpages, html pages, and interactive help text
>> all derived from the same semi-automated sources, always up to date and
>> much easier to read/discover/parse by human eyeballs.
>>
>> That's what I'd like to accomplish, ultimately.
>>
>> For now, I think this RFC set is not harmful and won't bother anybody.
>> It's definitely not worse than what we have now, fragility of removing
>> @var{} tokens and all.
> 
> Makes sense.  I just hope we can avoid duplicating work.
> 

I'm not going to proceed any further than this RFC without us having a
meeting to discuss the work. I'm willing to learn QAPI and do it a bit,
it's a thread I'd rather like to pull, but I don't want to duplicate any
work, no.

If that involves me reviewing patches when the tree opens, please CC me,
and let's have a chat!

In the meantime, what are your thoughts on patches 2-3 here?

--js
Markus Armbruster April 12, 2019, 8:04 a.m. UTC | #5
John Snow <jsnow@redhat.com> writes:

> On 4/11/19 2:22 AM, Markus Armbruster wrote:
>> John Snow <jsnow@redhat.com> writes:
[...]
>>> I suspect that work *IS* something that might brush up against / should
>>> use (or extend) QAPI code.
>> 
>> Seems likely.
>> 
>>> Then, I'd like to find a way to split qemu-img.texi into sub-command
>>> files and find a way to source the same "informative text" for both:
>>>
>>> (1) The texi output, as per usual, and
>>> (2) qemu-img subcommand --help
>>>
>>> such that --help, when passed as an argument to the subcommand, prints
>>> out help only relevant to the subcommand instead, e.g.
>>>
>>> `qemu-img check --help`
>>>
>>> might print the "common options" section only as it relates to commands
>>> actually used by the check command, then prints the check section of the
>>> texi as formatted for terminals.
>>>
>>> This way, we can have manpages, html pages, and interactive help text
>>> all derived from the same semi-automated sources, always up to date and
>>> much easier to read/discover/parse by human eyeballs.
>>>
>>> That's what I'd like to accomplish, ultimately.
>>>
>>> For now, I think this RFC set is not harmful and won't bother anybody.
>>> It's definitely not worse than what we have now, fragility of removing
>>> @var{} tokens and all.
>> 
>> Makes sense.  I just hope we can avoid duplicating work.
>> 
>
> I'm not going to proceed any further than this RFC without us having a
> meeting to discuss the work. I'm willing to learn QAPI and do it a bit,
> it's a thread I'd rather like to pull, but I don't want to duplicate any
> work, no.

I think the path forward depends on just how itchy the thing is for you.

I do want to resume the work on CLI QAPIfication.  Whenever I think my
queue is about to reach a state where I can ignore y'all for the
uninterrupted time the CLI project needs, something else lands in my
queue.  Right now, Kevin wants the next QAPI project to be "features
flags", and he has v1 patches to back this up[*].  I have additional
uses for feature flags in mind, which I expect to require more patches.

> If that involves me reviewing patches when the tree opens, please CC me,
> and let's have a chat!

Yes, let's talk.

> In the meantime, what are your thoughts on patches 2-3 here?

I'll try to have a closer look.


[*] Which I still have to review, but when my queue goes out of control,
I enter batch mode for better throughput, sacrificing latency.
diff mbox series

Patch

diff --git a/Makefile b/Makefile
index 04a0d45050..d203bb90dc 100644
--- a/Makefile
+++ b/Makefile
@@ -524,8 +524,8 @@  ifdef CONFIG_MPATH
 scsi/qemu-pr-helper$(EXESUF): LIBS += -ludev -lmultipath -lmpathpersist
 endif
 
-qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/scripts/hxtool
-	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"GEN","$@")
+qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.json $(SRC_PATH)/scripts/pxtool.py
+	$(call quiet-command,$(PYTOHN) $(SRC_PATH)/scripts/pxtool.py --macros $< > $@,"GEN","$@")
 
 qemu-ga$(EXESUF): LIBS = $(LIBS_QGA)
 qemu-ga$(EXESUF): QEMU_CFLAGS += -I qga/qapi-generated
@@ -918,8 +918,8 @@  qemu-monitor.texi: $(SRC_PATH)/hmp-commands.hx $(SRC_PATH)/scripts/hxtool
 qemu-monitor-info.texi: $(SRC_PATH)/hmp-commands-info.hx $(SRC_PATH)/scripts/hxtool
 	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@")
 
-qemu-img-cmds.texi: $(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/scripts/hxtool
-	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@")
+qemu-img-cmds.texi: $(SRC_PATH)/qemu-img-cmds.json $(SRC_PATH)/scripts/pxtool.py
+	$(call quiet-command,$(PYTON) $(SRC_PATH)/scripts/pxtool.py --texi $< > $@,"GEN","$@")
 
 docs/interop/qemu-qmp-qapi.texi: qapi/qapi-doc.texi
 	@cp -p $< $@
diff --git a/qemu-img-cmds.json b/qemu-img-cmds.json
new file mode 100644
index 0000000000..35d96f92a3
--- /dev/null
+++ b/qemu-img-cmds.json
@@ -0,0 +1,165 @@ 
+{
+    "amend": [
+        "[--object @var{objectdef}]",
+        "[--image-opts]",
+        "[-p]",
+        "[-q]",
+        "[-f @var{fmt}]",
+        "[-t @var{cache}]",
+        "-o @var{options}",
+        "@var{filename}"
+    ],
+    "bench": [
+        "[-c @var{count}]",
+        "[-d @var{depth}]",
+        "[-f @var{fmt}]",
+        "[--flush-interval=@var{flush_interval}]",
+        "[-n]",
+        "[--no-drain]",
+        "[-o @var{offset}]",
+        "[--pattern=@var{pattern}]",
+        "[-q]",
+        "[-s @var{buffer_size}]",
+        "[-S @var{step_size}]",
+        "[-t @var{cache}]",
+        "[-w]",
+        "[-U]",
+        "@var{filename}"
+    ],
+    "check": [
+        "[--object @var{objectdef}]",
+        "[--image-opts]",
+        "[-q]",
+        "[-f @var{fmt}]",
+        "[--output=@var{ofmt}]",
+        "[-r [leaks | all]]",
+        "[-T @var{src_cache}]",
+        "[-U]",
+        "@var{filename}"
+    ],
+    "commit": [
+        "[--object @var{objectdef}]",
+        "[--image-opts]",
+        "[-q]",
+        "[-f @var{fmt}]",
+        "[-t @var{cache}]",
+        "[-b @var{base}]",
+        "[-d]",
+        "[-p]",
+        "@var{filename}"
+    ],
+    "compare": [
+        "[--object @var{objectdef}]",
+        "[--image-opts]",
+        "[-f @var{fmt}]",
+        "[-F @var{fmt}]",
+        "[-T @var{src_cache}]",
+        "[-p]",
+        "[-q]",
+        "[-s]",
+        "[-U]",
+        "@var{filename1}",
+        "@var{filename2}"
+    ],
+    "convert": [
+        "[--object @var{objectdef}]",
+        "[--image-opts]",
+        "[--target-image-opts]",
+        "[-U]",
+        "[-C]",
+        "[-c]",
+        "[-p]",
+        "[-q]",
+        "[-n]",
+        "[-f @var{fmt}]",
+        "[-t @var{cache}]",
+        "[-T @var{src_cache}]",
+        "[-O @var{output_fmt}]",
+        "[-B @var{backing_file}]",
+        "[-o @var{options}]",
+        "[-l @var{snapshot_param}]",
+        "[-S @var{sparse_size}]",
+        "[-m @var{num_coroutines}]",
+        "[-W]",
+        "@var{filename}",
+        "[@var{filename2} [...]]",
+        "@var{output_filename}"
+    ],
+    "create": [
+        "[--object @var{objectdef}]",
+        "[-q]",
+        "[-f @var{fmt}]",
+        "[-b @var{backing_file}]",
+        "[-F @var{backing_fmt}]",
+        "[-u]",
+        "[-o @var{options}]",
+        "@var{filename}",
+        "[@var{size}]"
+    ],
+    "dd": [
+        "[--image-opts]",
+        "[-U]",
+        "[-f @var{fmt}]",
+        "[-O @var{output_fmt}]",
+        "[bs=@var{block_size}]",
+        "[count=@var{blocks}]",
+        "[skip=@var{blocks}]",
+        "if=@var{input}",
+        "of=@var{output}"
+    ],
+    "info": [
+        "[--object @var{objectdef}]",
+        "[--image-opts]",
+        "[-f @var{fmt}]",
+        "[--output=@var{ofmt}]",
+        "[--backing-chain]",
+        "[-U]",
+        "@var{filename}"
+    ],
+    "map": [
+        "[--object @var{objectdef}]",
+        "[--image-opts]",
+        "[-f @var{fmt}]",
+        "[--output=@var{ofmt}]",
+        "[-U]",
+        "@var{filename}"
+    ],
+    "measure": [
+        "[--output=@var{ofmt}]",
+        "[-O @var{output_fmt}]",
+        "[-o @var{options}]",
+        "[--size @var{N} | [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [-l @var{snapshot_param}] @var{filename}]"
+    ],
+    "snapshot": [
+        "[--object @var{objectdef}]",
+        "[--image-opts]",
+        "[-U]",
+        "[-q]",
+        "[-l | -a @var{snapshot} | -c @var{snapshot} | -d @var{snapshot}]",
+        "@var{filename}"
+    ],
+    "rebase": [
+        "[--object @var{objectdef}]",
+        "[--image-opts]",
+        "[-U]",
+        "[-q]",
+        "[-f @var{fmt}]",
+        "[-t @var{cache}]",
+        "[-T @var{src_cache}]",
+        "[-p]",
+        "[-u]",
+        "-b @var{backing_file}",
+        "[-F @var{backing_fmt}]",
+        "@var{filename}"
+    ],
+    "resize": [
+        "[--object @var{objectdef}]",
+        "[--image-opts]",
+        "[-f @var{fmt}]",
+        "[--preallocation=@var{prealloc}]",
+        "[-q]",
+        "[--shrink]",
+        "@var{filename}",
+        "[+ | -]@var{size}"
+    ]
+}
diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx
deleted file mode 100644
index 4b47f7495d..0000000000
--- a/qemu-img-cmds.hx
+++ /dev/null
@@ -1,102 +0,0 @@ 
-HXCOMM Keep the list of subcommands sorted by name.
-HXCOMM Use DEFHEADING() to define headings in both help text and texi
-HXCOMM Text between STEXI and ETEXI are copied to texi version and
-HXCOMM discarded from C version
-HXCOMM DEF(command, callback, arg_string) is used to construct
-HXCOMM command structures and help message.
-HXCOMM HXCOMM can be used for comments, discarded from both texi and C
-
-HXCOMM When amending the TEXI sections, please remember to copy the usage
-HXCOMM over to the per-command sections in qemu-img.texi.
-
-STEXI
-@table @option
-ETEXI
-
-DEF("amend", img_amend,
-    "amend [--object objectdef] [--image-opts] [-p] [-q] [-f fmt] [-t cache] -o options filename")
-STEXI
-@item amend [--object @var{objectdef}] [--image-opts] [-p] [-q] [-f @var{fmt}] [-t @var{cache}] -o @var{options} @var{filename}
-ETEXI
-
-DEF("bench", img_bench,
-    "bench [-c count] [-d depth] [-f fmt] [--flush-interval=flush_interval] [-n] [--no-drain] [-o offset] [--pattern=pattern] [-q] [-s buffer_size] [-S step_size] [-t cache] [-w] [-U] filename")
-STEXI
-@item bench [-c @var{count}] [-d @var{depth}] [-f @var{fmt}] [--flush-interval=@var{flush_interval}] [-n] [--no-drain] [-o @var{offset}] [--pattern=@var{pattern}] [-q] [-s @var{buffer_size}] [-S @var{step_size}] [-t @var{cache}] [-w] [-U] @var{filename}
-ETEXI
-
-DEF("check", img_check,
-    "check [--object objectdef] [--image-opts] [-q] [-f fmt] [--output=ofmt] [-r [leaks | all]] [-T src_cache] [-U] filename")
-STEXI
-@item check [--object @var{objectdef}] [--image-opts] [-q] [-f @var{fmt}] [--output=@var{ofmt}] [-r [leaks | all]] [-T @var{src_cache}] [-U] @var{filename}
-ETEXI
-
-DEF("commit", img_commit,
-    "commit [--object objectdef] [--image-opts] [-q] [-f fmt] [-t cache] [-b base] [-d] [-p] filename")
-STEXI
-@item commit [--object @var{objectdef}] [--image-opts] [-q] [-f @var{fmt}] [-t @var{cache}] [-b @var{base}] [-d] [-p] @var{filename}
-ETEXI
-
-DEF("compare", img_compare,
-    "compare [--object objectdef] [--image-opts] [-f fmt] [-F fmt] [-T src_cache] [-p] [-q] [-s] [-U] filename1 filename2")
-STEXI
-@item compare [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [-F @var{fmt}] [-T @var{src_cache}] [-p] [-q] [-s] [-U] @var{filename1} @var{filename2}
-ETEXI
-
-DEF("convert", img_convert,
-    "convert [--object objectdef] [--image-opts] [--target-image-opts] [-U] [-C] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-B backing_file] [-o options] [-l snapshot_param] [-S sparse_size] [-m num_coroutines] [-W] filename [filename2 [...]] output_filename")
-STEXI
-@item convert [--object @var{objectdef}] [--image-opts] [--target-image-opts] [-U] [-C] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-l @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W] @var{filename} [@var{filename2} [...]] @var{output_filename}
-ETEXI
-
-DEF("create", img_create,
-    "create [--object objectdef] [-q] [-f fmt] [-b backing_file] [-F backing_fmt] [-u] [-o options] filename [size]")
-STEXI
-@item create [--object @var{objectdef}] [-q] [-f @var{fmt}] [-b @var{backing_file}] [-F @var{backing_fmt}] [-u] [-o @var{options}] @var{filename} [@var{size}]
-ETEXI
-
-DEF("dd", img_dd,
-    "dd [--image-opts] [-U] [-f fmt] [-O output_fmt] [bs=block_size] [count=blocks] [skip=blocks] if=input of=output")
-STEXI
-@item dd [--image-opts] [-U] [-f @var{fmt}] [-O @var{output_fmt}] [bs=@var{block_size}] [count=@var{blocks}] [skip=@var{blocks}] if=@var{input} of=@var{output}
-ETEXI
-
-DEF("info", img_info,
-    "info [--object objectdef] [--image-opts] [-f fmt] [--output=ofmt] [--backing-chain] [-U] filename")
-STEXI
-@item info [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [--output=@var{ofmt}] [--backing-chain] [-U] @var{filename}
-ETEXI
-
-DEF("map", img_map,
-    "map [--object objectdef] [--image-opts] [-f fmt] [--output=ofmt] [-U] filename")
-STEXI
-@item map [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [--output=@var{ofmt}] [-U] @var{filename}
-ETEXI
-
-DEF("measure", img_measure,
-"measure [--output=ofmt] [-O output_fmt] [-o options] [--size N | [--object objectdef] [--image-opts] [-f fmt] [-l snapshot_param] filename]")
-STEXI
-@item measure [--output=@var{ofmt}] [-O @var{output_fmt}] [-o @var{options}] [--size @var{N} | [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [-l @var{snapshot_param}] @var{filename}]
-ETEXI
-
-DEF("snapshot", img_snapshot,
-    "snapshot [--object objectdef] [--image-opts] [-U] [-q] [-l | -a snapshot | -c snapshot | -d snapshot] filename")
-STEXI
-@item snapshot [--object @var{objectdef}] [--image-opts] [-U] [-q] [-l | -a @var{snapshot} | -c @var{snapshot} | -d @var{snapshot}] @var{filename}
-ETEXI
-
-DEF("rebase", img_rebase,
-    "rebase [--object objectdef] [--image-opts] [-U] [-q] [-f fmt] [-t cache] [-T src_cache] [-p] [-u] -b backing_file [-F backing_fmt] filename")
-STEXI
-@item rebase [--object @var{objectdef}] [--image-opts] [-U] [-q] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-p] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename}
-ETEXI
-
-DEF("resize", img_resize,
-    "resize [--object objectdef] [--image-opts] [-f fmt] [--preallocation=prealloc] [-q] [--shrink] filename [+ | -]size")
-STEXI
-@item resize [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [--preallocation=@var{prealloc}] [-q] [--shrink] @var{filename} [+ | -]@var{size}
-ETEXI
-
-STEXI
-@end table
-ETEXI
diff --git a/scripts/pxtool.py b/scripts/pxtool.py
new file mode 100755
index 0000000000..008fea839e
--- /dev/null
+++ b/scripts/pxtool.py
@@ -0,0 +1,49 @@ 
+#!/usr/bin/env python
+
+import argparse
+from collections import OrderedDict
+import json
+import re
+
+# Helpers:
+def scrub_texi_string(tstring):
+    return re.sub(r"@var{([^{}]*?)}", r"\1", tstring)
+
+def human_readable_usage(usage_list):
+    return scrub_texi_string(" ".join(usage_list))
+
+def callback_name(command):
+    return "img_{:s}".format(command)
+
+# Output modes:
+def generate_def_header(conf):
+    """Print DEF() macros to be used by qemu-img.c"""
+    for command, usage_strs in conf.items():
+        print("DEF(\"{}\", {}, \"{} {}\")".format(
+            command,
+            callback_name(command),
+            command,
+            human_readable_usage(usage_strs)))
+
+def generate_texi(conf):
+    """Generate texi command summary table"""
+    print("@table @option")
+    for command, usage_strs in conf.items():
+        usage = " ".join(usage_strs)
+        print("@item {} {}".format(command, usage))
+    print("@end table")
+
+if __name__ == '__main__':
+    parser = argparse.ArgumentParser(description="Generate qemu-img command information")
+    parser.add_argument("--macros", action="store_true", dest="macros")
+    parser.add_argument("--texi", action="store_true")
+    parser.add_argument("commands_json")
+    args = parser.parse_args()
+
+    with open(args.commands_json, "r") as f:
+        conf = json.load(f, object_pairs_hook=OrderedDict)
+
+    if args.macros:
+        generate_def_header(conf)
+    if args.texi:
+        generate_texi(conf)