All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH for-4.0 v7 00/27] Hi,
@ 2018-12-08 11:15 Marc-André Lureau
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 01/27] qapi: make sure osdep.h is included in type headers Marc-André Lureau
                   ` (28 more replies)
  0 siblings, 29 replies; 69+ messages in thread
From: Marc-André Lureau @ 2018-12-08 11:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Marc-André Lureau

This is the second part of the "add #if pre-processor conditions to
generated code" series, adding schema member conditions (roughly
16-38/49).

Members can be exploded as dictionnary with 'type'/'if' keys:

{ 'struct': 'TestIfStruct', 'data':
  { 'foo': 'int',
    'bar': { 'type': 'int', 'if': 'defined(TEST_IF_STRUCT_BAR)'} } }

Enum values can be exploded as dictionnary with 'type'/'if' keys:

{ 'enum': 'TestIfEnum', 'data':
  [ 'foo',
    { 'name' : 'bar', 'if': 'defined(TEST_IF_ENUM_BAR)' } ] }

v7:
- replaced osdep.h include by a #warning check
- renamed event_names/event_enum_members, enum_name/event_enum_ename
- move normalization after checking
- simplify make_enum_members()
- some tests changes
- rebase/conflicts tweaks
- squash documentation changes in related patches
- tweak generated documentation code & output
- modify commit comments, add rb tags

v6:
- subset of v5 series: add schema member conditions
- split "qapi: factor out check_known_keys()", error report improvements
- add a patch to "qapi: include osdep.h in type headers" to avoid
  potential build configuration change issues
- rebased, on top of Markus's qapi-next branch (first 4 patches,
  included for patchew testing)

Marc-André Lureau (27):
  qapi: make sure osdep.h is included in type headers
  qapi: do not define enumeration value explicitly
  qapi: rename QAPISchemaEnumType.values to .members
  qapi: change enum visitor and gen_enum* to take QAPISchemaMember
  tests: print enum type members more like object type members
  qapi: factor out checking for keys
  qapi: improve reporting of unknown or missing keys
  qapi: add a dictionary form with 'name' key for enum members
  qapi: add 'if' to enum members
  qapi-events: add 'if' condition to implicit event enum
  qapi: pass long form enum to make_enum_members
  qapi: rename allow_dict to allow_implicit
  qapi: add a dictionary form for TYPE
  qapi: add 'if' to implicit struct members
  qapi: add an error in case a discriminator is conditional
  qapi: add 'if' to union members
  qapi: simplify make_enum_members()
  tests/qapi: add command with condition on union argument
  qapi: add 'if' to alternate members
  tests/qapi: add command with condition on alternate argument
  qapi: add #if conditions to generated code members
  qapi: add 'If:' condition to enum values documentation
  qapi: add 'If:' condition to struct members documentation
  qapi: add condition to variants documentation
  qapi: break long lines at 'data' member
  qapi: add more conditions to SPICE
  qapi: add conditions to REPLICATION type/commands on the schema

 qapi/block-core.json                          |  26 +-
 qapi/char.json                                | 150 ++++++-----
 qapi/migration.json                           |  15 +-
 qapi/misc.json                                |   7 +-
 qapi/net.json                                 |   3 +-
 qapi/tpm.json                                 |   5 +-
 qapi/ui.json                                  |   3 +-
 scripts/qapi/common.py                        | 253 ++++++++++++------
 scripts/qapi/doc.py                           |  30 ++-
 scripts/qapi/events.py                        |  13 +-
 scripts/qapi/introspect.py                    |  16 +-
 scripts/qapi/types.py                         |  13 +-
 scripts/qapi/visit.py                         |   8 +-
 migration/colo.c                              |  16 +-
 monitor.c                                     |   5 -
 docs/devel/qapi-code-gen.txt                  |  19 ++
 tests/Makefile.include                        |  11 +-
 tests/qapi-schema/alternate-base.err          |   1 +
 tests/qapi-schema/alternate-invalid-dict.err  |   1 +
 ...ember.exit => alternate-invalid-dict.exit} |   0
 tests/qapi-schema/alternate-invalid-dict.json |   4 +
 ...-member.out => alternate-invalid-dict.out} |   0
 tests/qapi-schema/comments.out                |  14 +-
 tests/qapi-schema/doc-bad-section.out         |  13 +-
 tests/qapi-schema/doc-good.json               |  11 +-
 tests/qapi-schema/doc-good.out                |  22 +-
 tests/qapi-schema/doc-good.texi               |   7 +-
 tests/qapi-schema/double-type.err             |   1 +
 tests/qapi-schema/empty.out                   |   9 +-
 tests/qapi-schema/enum-bad-member.err         |   1 +
 tests/qapi-schema/enum-bad-member.exit        |   1 +
 tests/qapi-schema/enum-bad-member.json        |   2 +
 tests/qapi-schema/enum-bad-member.out         |   0
 .../qapi-schema/enum-dict-member-unknown.err  |   2 +
 .../qapi-schema/enum-dict-member-unknown.exit |   1 +
 .../qapi-schema/enum-dict-member-unknown.json |   2 +
 .../qapi-schema/enum-dict-member-unknown.out  |   0
 tests/qapi-schema/enum-dict-member.err        |   1 -
 tests/qapi-schema/enum-dict-member.json       |   2 -
 tests/qapi-schema/enum-if-invalid.err         |   1 +
 tests/qapi-schema/enum-if-invalid.exit        |   1 +
 tests/qapi-schema/enum-if-invalid.json        |   3 +
 tests/qapi-schema/enum-if-invalid.out         |   0
 tests/qapi-schema/event-case.out              |   9 +-
 .../qapi-schema/event-member-invalid-dict.err |   1 +
 .../event-member-invalid-dict.exit            |   1 +
 .../event-member-invalid-dict.json            |   2 +
 .../qapi-schema/event-member-invalid-dict.out |   0
 tests/qapi-schema/event-nest-struct.json      |   2 +-
 .../flat-union-inline-invalid-dict.err        |   1 +
 .../flat-union-inline-invalid-dict.exit       |   1 +
 .../flat-union-inline-invalid-dict.json       |  11 +
 .../flat-union-inline-invalid-dict.out        |   0
 tests/qapi-schema/flat-union-inline.json      |   2 +-
 .../flat-union-invalid-if-discriminator.err   |   1 +
 .../flat-union-invalid-if-discriminator.exit  |   1 +
 .../flat-union-invalid-if-discriminator.json  |  17 ++
 .../flat-union-invalid-if-discriminator.out   |   0
 tests/qapi-schema/ident-with-escape.out       |   9 +-
 tests/qapi-schema/include-relpath.out         |  14 +-
 tests/qapi-schema/include-repetition.out      |  14 +-
 tests/qapi-schema/include-simple.out          |  14 +-
 tests/qapi-schema/indented-expr.out           |   9 +-
 .../nested-struct-data-invalid-dict.err       |   1 +
 .../nested-struct-data-invalid-dict.exit      |   1 +
 .../nested-struct-data-invalid-dict.json      |   3 +
 .../nested-struct-data-invalid-dict.out       |   0
 tests/qapi-schema/nested-struct-data.json     |   2 +-
 tests/qapi-schema/qapi-schema-test.json       |  39 ++-
 tests/qapi-schema/qapi-schema-test.out        |  74 ++++-
 .../struct-member-invalid-dict.err            |   1 +
 .../struct-member-invalid-dict.exit           |   1 +
 .../struct-member-invalid-dict.json           |   3 +
 .../struct-member-invalid-dict.out            |   0
 tests/qapi-schema/test-qapi.py                |   9 +-
 .../qapi-schema/union-branch-invalid-dict.err |   1 +
 .../union-branch-invalid-dict.exit            |   1 +
 .../union-branch-invalid-dict.json            |   4 +
 .../qapi-schema/union-branch-invalid-dict.out |   0
 tests/qapi-schema/unknown-expr-key.err        |   3 +-
 tests/qapi-schema/unknown-expr-key.json       |   2 +-
 81 files changed, 681 insertions(+), 266 deletions(-)
 create mode 100644 tests/qapi-schema/alternate-invalid-dict.err
 rename tests/qapi-schema/{enum-dict-member.exit => alternate-invalid-dict.exit} (100%)
 create mode 100644 tests/qapi-schema/alternate-invalid-dict.json
 rename tests/qapi-schema/{enum-dict-member.out => alternate-invalid-dict.out} (100%)
 create mode 100644 tests/qapi-schema/enum-bad-member.err
 create mode 100644 tests/qapi-schema/enum-bad-member.exit
 create mode 100644 tests/qapi-schema/enum-bad-member.json
 create mode 100644 tests/qapi-schema/enum-bad-member.out
 create mode 100644 tests/qapi-schema/enum-dict-member-unknown.err
 create mode 100644 tests/qapi-schema/enum-dict-member-unknown.exit
 create mode 100644 tests/qapi-schema/enum-dict-member-unknown.json
 create mode 100644 tests/qapi-schema/enum-dict-member-unknown.out
 delete mode 100644 tests/qapi-schema/enum-dict-member.err
 delete mode 100644 tests/qapi-schema/enum-dict-member.json
 create mode 100644 tests/qapi-schema/enum-if-invalid.err
 create mode 100644 tests/qapi-schema/enum-if-invalid.exit
 create mode 100644 tests/qapi-schema/enum-if-invalid.json
 create mode 100644 tests/qapi-schema/enum-if-invalid.out
 create mode 100644 tests/qapi-schema/event-member-invalid-dict.err
 create mode 100644 tests/qapi-schema/event-member-invalid-dict.exit
 create mode 100644 tests/qapi-schema/event-member-invalid-dict.json
 create mode 100644 tests/qapi-schema/event-member-invalid-dict.out
 create mode 100644 tests/qapi-schema/flat-union-inline-invalid-dict.err
 create mode 100644 tests/qapi-schema/flat-union-inline-invalid-dict.exit
 create mode 100644 tests/qapi-schema/flat-union-inline-invalid-dict.json
 create mode 100644 tests/qapi-schema/flat-union-inline-invalid-dict.out
 create mode 100644 tests/qapi-schema/flat-union-invalid-if-discriminator.err
 create mode 100644 tests/qapi-schema/flat-union-invalid-if-discriminator.exit
 create mode 100644 tests/qapi-schema/flat-union-invalid-if-discriminator.json
 create mode 100644 tests/qapi-schema/flat-union-invalid-if-discriminator.out
 create mode 100644 tests/qapi-schema/nested-struct-data-invalid-dict.err
 create mode 100644 tests/qapi-schema/nested-struct-data-invalid-dict.exit
 create mode 100644 tests/qapi-schema/nested-struct-data-invalid-dict.json
 create mode 100644 tests/qapi-schema/nested-struct-data-invalid-dict.out
 create mode 100644 tests/qapi-schema/struct-member-invalid-dict.err
 create mode 100644 tests/qapi-schema/struct-member-invalid-dict.exit
 create mode 100644 tests/qapi-schema/struct-member-invalid-dict.json
 create mode 100644 tests/qapi-schema/struct-member-invalid-dict.out
 create mode 100644 tests/qapi-schema/union-branch-invalid-dict.err
 create mode 100644 tests/qapi-schema/union-branch-invalid-dict.exit
 create mode 100644 tests/qapi-schema/union-branch-invalid-dict.json
 create mode 100644 tests/qapi-schema/union-branch-invalid-dict.out

-- 
2.20.0.rc1

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

* [Qemu-devel] [PATCH for-4.0 v7 01/27] qapi: make sure osdep.h is included in type headers
  2018-12-08 11:15 [Qemu-devel] [PATCH for-4.0 v7 00/27] Hi, Marc-André Lureau
@ 2018-12-08 11:15 ` Marc-André Lureau
  2018-12-10  9:52   ` Markus Armbruster
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 02/27] qapi: do not define enumeration value explicitly Marc-André Lureau
                   ` (27 subsequent siblings)
  28 siblings, 1 reply; 69+ messages in thread
From: Marc-André Lureau @ 2018-12-08 11:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Marc-André Lureau, Michael Roth

Now that the schema can be configured, it is crucial that all types
are configured the same. Make sure config-host.h is included, by
checking osdep.h inclusion. The build-sys tracks the dependency and
rebuilds the types if the configuration changed.

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

diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py
index fd7808103c..3bb9cb6d47 100644
--- a/scripts/qapi/types.py
+++ b/scripts/qapi/types.py
@@ -201,6 +201,9 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor):
 ''',
                                       types=types, visit=visit))
         self._genh.preamble_add(mcgen('''
+#ifndef QEMU_OSDEP_H
+#error Please include osdep.h first!
+#endif
 #include "qapi/qapi-builtin-types.h"
 '''))
 
-- 
2.20.0.rc1

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

* [Qemu-devel] [PATCH for-4.0 v7 02/27] qapi: do not define enumeration value explicitly
  2018-12-08 11:15 [Qemu-devel] [PATCH for-4.0 v7 00/27] Hi, Marc-André Lureau
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 01/27] qapi: make sure osdep.h is included in type headers Marc-André Lureau
@ 2018-12-08 11:15 ` Marc-André Lureau
  2018-12-12  8:52   ` Markus Armbruster
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 03/27] qapi: rename QAPISchemaEnumType.values to .members Marc-André Lureau
                   ` (26 subsequent siblings)
  28 siblings, 1 reply; 69+ messages in thread
From: Marc-André Lureau @ 2018-12-08 11:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Marc-André Lureau, Michael Roth

The C standard has the initial value at 0 and the subsequent values
incremented by 1. No need to set this explicitely.

This will prevent from artificial "gaps" when compiling out some enum
values and having unnecessarily large MAX values & enums arrays, or
simplifying iterating over valid enum values.

Whenever config-host.h is changed, all the enum/types are recompiled.

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

diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index 7b62a4c7b0..7ea0cf5139 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -2045,14 +2045,11 @@ typedef enum %(c_name)s {
 ''',
                 c_name=c_name(name))
 
-    i = 0
     for value in enum_values:
         ret += mcgen('''
-    %(c_enum)s = %(i)d,
+    %(c_enum)s,
 ''',
-                     c_enum=c_enum_const(name, value, prefix),
-                     i=i)
-        i += 1
+                     c_enum=c_enum_const(name, value, prefix))
 
     ret += mcgen('''
 } %(c_name)s;
-- 
2.20.0.rc1

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

* [Qemu-devel] [PATCH for-4.0 v7 03/27] qapi: rename QAPISchemaEnumType.values to .members
  2018-12-08 11:15 [Qemu-devel] [PATCH for-4.0 v7 00/27] Hi, Marc-André Lureau
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 01/27] qapi: make sure osdep.h is included in type headers Marc-André Lureau
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 02/27] qapi: do not define enumeration value explicitly Marc-André Lureau
@ 2018-12-08 11:15 ` Marc-André Lureau
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 04/27] qapi: change enum visitor and gen_enum* to take QAPISchemaMember Marc-André Lureau
                   ` (25 subsequent siblings)
  28 siblings, 0 replies; 69+ messages in thread
From: Marc-André Lureau @ 2018-12-08 11:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Marc-André Lureau, Michael Roth

Rename QAPISchemaEnumType.values and related variables to members.
Makes sense ever since commit 93bda4dd4 changed .values from list of
string to list of QAPISchemaMember. Obvious no-op.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
---
 scripts/qapi/common.py | 24 ++++++++++++------------
 1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index 7ea0cf5139..55c914ec44 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -1161,22 +1161,22 @@ class QAPISchemaBuiltinType(QAPISchemaType):
 
 
 class QAPISchemaEnumType(QAPISchemaType):
-    def __init__(self, name, info, doc, ifcond, values, prefix):
+    def __init__(self, name, info, doc, ifcond, members, prefix):
         QAPISchemaType.__init__(self, name, info, doc, ifcond)
-        for v in values:
-            assert isinstance(v, QAPISchemaMember)
-            v.set_owner(name)
+        for m in members:
+            assert isinstance(m, QAPISchemaMember)
+            m.set_owner(name)
         assert prefix is None or isinstance(prefix, str)
-        self.values = values
+        self.members = members
         self.prefix = prefix
 
     def check(self, schema):
         QAPISchemaType.check(self, schema)
         seen = {}
-        for v in self.values:
-            v.check_clash(self.info, seen)
+        for m in self.members:
+            m.check_clash(self.info, seen)
             if self.doc:
-                self.doc.connect_member(v)
+                self.doc.connect_member(m)
 
     def is_implicit(self):
         # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
@@ -1186,7 +1186,7 @@ class QAPISchemaEnumType(QAPISchemaType):
         return c_name(self.name)
 
     def member_names(self):
-        return [v.name for v in self.values]
+        return [m.name for m in self.members]
 
     def json_type(self):
         return 'string'
@@ -1403,9 +1403,9 @@ class QAPISchemaObjectTypeVariants(object):
         if self._tag_name:    # flat union
             # branches that are not explicitly covered get an empty type
             cases = set([v.name for v in self.variants])
-            for val in self.tag_member.type.values:
-                if val.name not in cases:
-                    v = QAPISchemaObjectTypeVariant(val.name, 'q_empty')
+            for m in self.tag_member.type.members:
+                if m.name not in cases:
+                    v = QAPISchemaObjectTypeVariant(m.name, 'q_empty')
                     v.set_owner(self.tag_member.owner)
                     self.variants.append(v)
         for v in self.variants:
-- 
2.20.0.rc1

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

* [Qemu-devel] [PATCH for-4.0 v7 04/27] qapi: change enum visitor and gen_enum* to take QAPISchemaMember
  2018-12-08 11:15 [Qemu-devel] [PATCH for-4.0 v7 00/27] Hi, Marc-André Lureau
                   ` (2 preceding siblings ...)
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 03/27] qapi: rename QAPISchemaEnumType.values to .members Marc-André Lureau
@ 2018-12-08 11:15 ` Marc-André Lureau
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 05/27] tests: print enum type members more like object type members Marc-André Lureau
                   ` (24 subsequent siblings)
  28 siblings, 0 replies; 69+ messages in thread
From: Marc-André Lureau @ 2018-12-08 11:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Marc-André Lureau, Michael Roth

This will allow to add and access more properties associated with enum
values/members, like the associated 'if' condition. We may want to
have a specialized type QAPISchemaEnumMember, for now this will do.

Modify gen_enum() and gen_enum_lookup() for the same reason.

Suggested-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
---
 scripts/qapi/common.py         | 22 +++++++++++-----------
 scripts/qapi/doc.py            |  2 +-
 scripts/qapi/events.py         | 13 +++++++------
 scripts/qapi/introspect.py     |  5 +++--
 scripts/qapi/types.py          |  6 +++---
 scripts/qapi/visit.py          |  2 +-
 tests/qapi-schema/test-qapi.py |  4 ++--
 7 files changed, 28 insertions(+), 26 deletions(-)

diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index 55c914ec44..1fa2f79990 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -1063,7 +1063,7 @@ class QAPISchemaVisitor(object):
     def visit_builtin_type(self, name, info, json_type):
         pass
 
-    def visit_enum_type(self, name, info, ifcond, values, prefix):
+    def visit_enum_type(self, name, info, ifcond, members, prefix):
         pass
 
     def visit_array_type(self, name, info, ifcond, element_type):
@@ -1193,7 +1193,7 @@ class QAPISchemaEnumType(QAPISchemaType):
 
     def visit(self, visitor):
         visitor.visit_enum_type(self.name, self.info, self.ifcond,
-                                self.member_names(), self.prefix)
+                                self.members, self.prefix)
 
 
 class QAPISchemaArrayType(QAPISchemaType):
@@ -2012,19 +2012,19 @@ def _wrap_ifcond(ifcond, before, after):
     return out
 
 
-def gen_enum_lookup(name, values, prefix=None):
+def gen_enum_lookup(name, members, prefix=None):
     ret = mcgen('''
 
 const QEnumLookup %(c_name)s_lookup = {
     .array = (const char *const[]) {
 ''',
                 c_name=c_name(name))
-    for value in values:
-        index = c_enum_const(name, value, prefix)
+    for m in members:
+        index = c_enum_const(name, m.name, prefix)
         ret += mcgen('''
-        [%(index)s] = "%(value)s",
+        [%(index)s] = "%(name)s",
 ''',
-                     index=index, value=value)
+                     index=index, name=m.name)
 
     ret += mcgen('''
     },
@@ -2035,9 +2035,9 @@ const QEnumLookup %(c_name)s_lookup = {
     return ret
 
 
-def gen_enum(name, values, prefix=None):
+def gen_enum(name, members, prefix=None):
     # append automatically generated _MAX value
-    enum_values = values + ['_MAX']
+    enum_members = members + [QAPISchemaMember('_MAX')]
 
     ret = mcgen('''
 
@@ -2045,11 +2045,11 @@ typedef enum %(c_name)s {
 ''',
                 c_name=c_name(name))
 
-    for value in enum_values:
+    for m in enum_members:
         ret += mcgen('''
     %(c_enum)s,
 ''',
-                     c_enum=c_enum_const(name, value, prefix))
+                     c_enum=c_enum_const(name, m.name, prefix))
 
     ret += mcgen('''
 } %(c_name)s;
diff --git a/scripts/qapi/doc.py b/scripts/qapi/doc.py
index 987fd3c943..76cb186ff9 100755
--- a/scripts/qapi/doc.py
+++ b/scripts/qapi/doc.py
@@ -206,7 +206,7 @@ class QAPISchemaGenDocVisitor(qapi.common.QAPISchemaVisitor):
     def write(self, output_dir):
         self._gen.write(output_dir, self._prefix + 'qapi-doc.texi')
 
-    def visit_enum_type(self, name, info, ifcond, values, prefix):
+    def visit_enum_type(self, name, info, ifcond, members, prefix):
         doc = self.cur_doc
         self._gen.add(TYPE_FMT(type='Enum',
                                name=doc.symbol,
diff --git a/scripts/qapi/events.py b/scripts/qapi/events.py
index 2ed7902424..f1b88d8786 100644
--- a/scripts/qapi/events.py
+++ b/scripts/qapi/events.py
@@ -143,8 +143,8 @@ class QAPISchemaGenEventVisitor(QAPISchemaModularCVisitor):
         QAPISchemaModularCVisitor.__init__(
             self, prefix, 'qapi-events',
             ' * Schema-defined QAPI/QMP events', __doc__)
-        self._enum_name = c_name(prefix + 'QAPIEvent', protect=False)
-        self._event_names = []
+        self._event_enum_name = c_name(prefix + 'QAPIEvent', protect=False)
+        self._event_enum_members = []
 
     def _begin_module(self, name):
         types = self._module_basename('qapi-types', name)
@@ -170,15 +170,16 @@ class QAPISchemaGenEventVisitor(QAPISchemaModularCVisitor):
 
     def visit_end(self):
         (genc, genh) = self._module[self._main_module]
-        genh.add(gen_enum(self._enum_name, self._event_names))
-        genc.add(gen_enum_lookup(self._enum_name, self._event_names))
+        genh.add(gen_enum(self._event_enum_name, self._event_enum_members))
+        genc.add(gen_enum_lookup(self._event_enum_name,
+                                 self._event_enum_members))
 
     def visit_event(self, name, info, ifcond, arg_type, boxed):
         with ifcontext(ifcond, self._genh, self._genc):
             self._genh.add(gen_event_send_decl(name, arg_type, boxed))
             self._genc.add(gen_event_send(name, arg_type, boxed,
-                                          self._enum_name))
-        self._event_names.append(name)
+                                          self._event_enum_name))
+        self._event_enum_members.append(QAPISchemaMember(name))
 
 
 def gen_events(schema, output_dir, prefix):
diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py
index 67d6106f77..417625d54b 100644
--- a/scripts/qapi/introspect.py
+++ b/scripts/qapi/introspect.py
@@ -174,8 +174,9 @@ const QLitObject %(c_name)s = %(c_string)s;
     def visit_builtin_type(self, name, info, json_type):
         self._gen_qlit(name, 'builtin', {'json-type': json_type}, [])
 
-    def visit_enum_type(self, name, info, ifcond, values, prefix):
-        self._gen_qlit(name, 'enum', {'values': values}, ifcond)
+    def visit_enum_type(self, name, info, ifcond, members, prefix):
+        self._gen_qlit(name, 'enum',
+                       {'values': [m.name for m in members]}, ifcond)
 
     def visit_array_type(self, name, info, ifcond, element_type):
         element = self._use_type(element_type)
diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py
index 3bb9cb6d47..0404710bbd 100644
--- a/scripts/qapi/types.py
+++ b/scripts/qapi/types.py
@@ -215,10 +215,10 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor):
         self._genh.add(gen_type_cleanup_decl(name))
         self._genc.add(gen_type_cleanup(name))
 
-    def visit_enum_type(self, name, info, ifcond, values, prefix):
+    def visit_enum_type(self, name, info, ifcond, members, prefix):
         with ifcontext(ifcond, self._genh, self._genc):
-            self._genh.preamble_add(gen_enum(name, values, prefix))
-            self._genc.add(gen_enum_lookup(name, values, prefix))
+            self._genh.preamble_add(gen_enum(name, members, prefix))
+            self._genc.add(gen_enum_lookup(name, members, prefix))
 
     def visit_array_type(self, name, info, ifcond, element_type):
         with ifcontext(ifcond, self._genh, self._genc):
diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py
index 460cf12989..24f85a2e85 100644
--- a/scripts/qapi/visit.py
+++ b/scripts/qapi/visit.py
@@ -310,7 +310,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor):
 ''',
                                       types=types))
 
-    def visit_enum_type(self, name, info, ifcond, values, prefix):
+    def visit_enum_type(self, name, info, ifcond, members, prefix):
         with ifcontext(ifcond, self._genh, self._genc):
             self._genh.add(gen_visit_decl(name, scalar=True))
             self._genc.add(gen_visit_enum(name))
diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
index cea21c773a..27f776693e 100644
--- a/tests/qapi-schema/test-qapi.py
+++ b/tests/qapi-schema/test-qapi.py
@@ -23,8 +23,8 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
     def visit_include(self, name, info):
         print('include %s' % name)
 
-    def visit_enum_type(self, name, info, ifcond, values, prefix):
-        print('enum %s %s' % (name, values))
+    def visit_enum_type(self, name, info, ifcond, members, prefix):
+        print('enum %s %s' % (name, [m.name for m in members]))
         if prefix:
             print('    prefix %s' % prefix)
         self._print_if(ifcond)
-- 
2.20.0.rc1

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

* [Qemu-devel] [PATCH for-4.0 v7 05/27] tests: print enum type members more like object type members
  2018-12-08 11:15 [Qemu-devel] [PATCH for-4.0 v7 00/27] Hi, Marc-André Lureau
                   ` (3 preceding siblings ...)
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 04/27] qapi: change enum visitor and gen_enum* to take QAPISchemaMember Marc-André Lureau
@ 2018-12-08 11:15 ` Marc-André Lureau
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 06/27] qapi: factor out checking for keys Marc-André Lureau
                   ` (23 subsequent siblings)
  28 siblings, 0 replies; 69+ messages in thread
From: Marc-André Lureau @ 2018-12-08 11:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Marc-André Lureau, Michael Roth

Commit 93bda4dd461 changed the internal representation of enum type
members from str to QAPISchemaMember, but we still print only a
string.  Has been good enough, as the name is the member's only
attribute of interest, but that's about to change.  To prepare, print
them more like object type members.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
---
 tests/qapi-schema/comments.out           | 14 ++++++-
 tests/qapi-schema/doc-bad-section.out    | 13 +++++-
 tests/qapi-schema/doc-good.out           | 17 ++++++--
 tests/qapi-schema/empty.out              |  9 ++++-
 tests/qapi-schema/event-case.out         |  9 ++++-
 tests/qapi-schema/ident-with-escape.out  |  9 ++++-
 tests/qapi-schema/include-relpath.out    | 14 ++++++-
 tests/qapi-schema/include-repetition.out | 14 ++++++-
 tests/qapi-schema/include-simple.out     | 14 ++++++-
 tests/qapi-schema/indented-expr.out      |  9 ++++-
 tests/qapi-schema/qapi-schema-test.out   | 50 +++++++++++++++++++-----
 tests/qapi-schema/test-qapi.py           |  4 +-
 12 files changed, 149 insertions(+), 27 deletions(-)

diff --git a/tests/qapi-schema/comments.out b/tests/qapi-schema/comments.out
index 8d2f1ce8a2..d1abc4b5a1 100644
--- a/tests/qapi-schema/comments.out
+++ b/tests/qapi-schema/comments.out
@@ -1,5 +1,15 @@
 object q_empty
-enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
+enum QType
     prefix QTYPE
+    member none
+    member qnull
+    member qnum
+    member qstring
+    member qdict
+    member qlist
+    member qbool
 module comments.json
-enum Status ['good', 'bad', 'ugly']
+enum Status
+    member good
+    member bad
+    member ugly
diff --git a/tests/qapi-schema/doc-bad-section.out b/tests/qapi-schema/doc-bad-section.out
index cd28721568..db8014eed0 100644
--- a/tests/qapi-schema/doc-bad-section.out
+++ b/tests/qapi-schema/doc-bad-section.out
@@ -1,8 +1,17 @@
 object q_empty
-enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
+enum QType
     prefix QTYPE
+    member none
+    member qnull
+    member qnum
+    member qstring
+    member qdict
+    member qlist
+    member qbool
 module doc-bad-section.json
-enum Enum ['one', 'two']
+enum Enum
+    member one
+    member two
 doc symbol=Enum
     body=
 == Produces *invalid* texinfo
diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out
index 35f3f1164c..c2fc5c774a 100644
--- a/tests/qapi-schema/doc-good.out
+++ b/tests/qapi-schema/doc-good.out
@@ -1,8 +1,17 @@
 object q_empty
-enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
+enum QType
     prefix QTYPE
+    member none
+    member qnull
+    member qnum
+    member qstring
+    member qdict
+    member qlist
+    member qbool
 module doc-good.json
-enum Enum ['one', 'two']
+enum Enum
+    member one
+    member two
     if ['defined(IFCOND)']
 object Base
     member base1: Enum optional=False
@@ -18,7 +27,9 @@ object q_obj_Variant1-wrapper
     member data: Variant1 optional=False
 object q_obj_Variant2-wrapper
     member data: Variant2 optional=False
-enum SugaredUnionKind ['one', 'two']
+enum SugaredUnionKind
+    member one
+    member two
 object SugaredUnion
     member type: SugaredUnionKind optional=False
     tag type
diff --git a/tests/qapi-schema/empty.out b/tests/qapi-schema/empty.out
index 0ec234eec4..5483cb7bc6 100644
--- a/tests/qapi-schema/empty.out
+++ b/tests/qapi-schema/empty.out
@@ -1,3 +1,10 @@
 object q_empty
-enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
+enum QType
     prefix QTYPE
+    member none
+    member qnull
+    member qnum
+    member qstring
+    member qdict
+    member qlist
+    member qbool
diff --git a/tests/qapi-schema/event-case.out b/tests/qapi-schema/event-case.out
index 88c0964917..f69d4ffe4e 100644
--- a/tests/qapi-schema/event-case.out
+++ b/tests/qapi-schema/event-case.out
@@ -1,6 +1,13 @@
 object q_empty
-enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
+enum QType
     prefix QTYPE
+    member none
+    member qnull
+    member qnum
+    member qstring
+    member qdict
+    member qlist
+    member qbool
 module event-case.json
 event oops None
    boxed=False
diff --git a/tests/qapi-schema/ident-with-escape.out b/tests/qapi-schema/ident-with-escape.out
index 24c976f473..7f891f7e90 100644
--- a/tests/qapi-schema/ident-with-escape.out
+++ b/tests/qapi-schema/ident-with-escape.out
@@ -1,6 +1,13 @@
 object q_empty
-enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
+enum QType
     prefix QTYPE
+    member none
+    member qnull
+    member qnum
+    member qstring
+    member qdict
+    member qlist
+    member qbool
 module ident-with-escape.json
 object q_obj_fooA-arg
     member bar1: str optional=False
diff --git a/tests/qapi-schema/include-relpath.out b/tests/qapi-schema/include-relpath.out
index ebbabd7a18..783ccfc855 100644
--- a/tests/qapi-schema/include-relpath.out
+++ b/tests/qapi-schema/include-relpath.out
@@ -1,9 +1,19 @@
 object q_empty
-enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
+enum QType
     prefix QTYPE
+    member none
+    member qnull
+    member qnum
+    member qstring
+    member qdict
+    member qlist
+    member qbool
 module include-relpath.json
 include include/relpath.json
 module include/relpath.json
 include include-relpath-sub.json
 module include-relpath-sub.json
-enum Status ['good', 'bad', 'ugly']
+enum Status
+    member good
+    member bad
+    member ugly
diff --git a/tests/qapi-schema/include-repetition.out b/tests/qapi-schema/include-repetition.out
index 7235e055bc..d45977ee56 100644
--- a/tests/qapi-schema/include-repetition.out
+++ b/tests/qapi-schema/include-repetition.out
@@ -1,10 +1,20 @@
 object q_empty
-enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
+enum QType
     prefix QTYPE
+    member none
+    member qnull
+    member qnum
+    member qstring
+    member qdict
+    member qlist
+    member qbool
 module include-repetition.json
 include comments.json
 module comments.json
-enum Status ['good', 'bad', 'ugly']
+enum Status
+    member good
+    member bad
+    member ugly
 module include-repetition.json
 include include-repetition-sub.json
 module include-repetition-sub.json
diff --git a/tests/qapi-schema/include-simple.out b/tests/qapi-schema/include-simple.out
index 006f723eeb..1afe20802a 100644
--- a/tests/qapi-schema/include-simple.out
+++ b/tests/qapi-schema/include-simple.out
@@ -1,7 +1,17 @@
 object q_empty
-enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
+enum QType
     prefix QTYPE
+    member none
+    member qnull
+    member qnum
+    member qstring
+    member qdict
+    member qlist
+    member qbool
 module include-simple.json
 include include-simple-sub.json
 module include-simple-sub.json
-enum Status ['good', 'bad', 'ugly']
+enum Status
+    member good
+    member bad
+    member ugly
diff --git a/tests/qapi-schema/indented-expr.out b/tests/qapi-schema/indented-expr.out
index bd8a48630e..c0cf3243f3 100644
--- a/tests/qapi-schema/indented-expr.out
+++ b/tests/qapi-schema/indented-expr.out
@@ -1,6 +1,13 @@
 object q_empty
-enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
+enum QType
     prefix QTYPE
+    member none
+    member qnull
+    member qnum
+    member qstring
+    member qdict
+    member qlist
+    member qbool
 module indented-expr.json
 command eins None -> None
    gen=True success_response=True boxed=False oob=False preconfig=False
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index 218ac7d556..39cd2b0fc3 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -1,6 +1,13 @@
 object q_empty
-enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
+enum QType
     prefix QTYPE
+    member none
+    member qnull
+    member qnum
+    member qstring
+    member qdict
+    member qlist
+    member qbool
 module qapi-schema-test.json
 object TestStruct
     member integer: int optional=False
@@ -11,19 +18,25 @@ object NestedEnumsOne
     member enum2: EnumOne optional=True
     member enum3: EnumOne optional=False
     member enum4: EnumOne optional=True
-enum MyEnum []
+enum MyEnum
 object Empty1
 object Empty2
     base Empty1
 command user_def_cmd0 Empty2 -> Empty2
    gen=True success_response=True boxed=False oob=False preconfig=False
-enum QEnumTwo ['value1', 'value2']
+enum QEnumTwo
     prefix QENUM_TWO
+    member value1
+    member value2
 object UserDefOne
     base UserDefZero
     member string: str optional=False
     member enum1: EnumOne optional=True
-enum EnumOne ['value1', 'value2', 'value3', 'value4']
+enum EnumOne
+    member value1
+    member value2
+    member value3
+    member value4
 object UserDefZero
     member integer: int optional=False
 object UserDefTwoDictDict
@@ -127,7 +140,21 @@ object q_obj_sizeList-wrapper
     member data: sizeList optional=False
 object q_obj_anyList-wrapper
     member data: anyList optional=False
-enum UserDefNativeListUnionKind ['integer', 's8', 's16', 's32', 's64', 'u8', 'u16', 'u32', 'u64', 'number', 'boolean', 'string', 'sizes', 'any']
+enum UserDefNativeListUnionKind
+    member integer
+    member s8
+    member s16
+    member s32
+    member s64
+    member u8
+    member u16
+    member u32
+    member u64
+    member number
+    member boolean
+    member string
+    member sizes
+    member any
 object UserDefNativeListUnion
     member type: UserDefNativeListUnionKind optional=False
     tag type
@@ -204,7 +231,8 @@ event EVENT_E UserDefZero
    boxed=True
 event EVENT_F UserDefAlternate
    boxed=True
-enum __org.qemu_x-Enum ['__org.qemu_x-value']
+enum __org.qemu_x-Enum
+    member __org.qemu_x-value
 object __org.qemu_x-Base
     member __org.qemu_x-member1: __org.qemu_x-Enum optional=False
 object __org.qemu_x-Struct
@@ -213,7 +241,8 @@ object __org.qemu_x-Struct
     member wchar-t: int optional=True
 object q_obj_str-wrapper
     member data: str optional=False
-enum __org.qemu_x-Union1Kind ['__org.qemu_x-branch']
+enum __org.qemu_x-Union1Kind
+    member __org.qemu_x-branch
 object __org.qemu_x-Union1
     member type: __org.qemu_x-Union1Kind optional=False
     tag type
@@ -240,11 +269,14 @@ command __org.qemu_x-command q_obj___org.qemu_x-command-arg -> __org.qemu_x-Unio
 object TestIfStruct
     member foo: int optional=False
     if ['defined(TEST_IF_STRUCT)']
-enum TestIfEnum ['foo', 'bar']
+enum TestIfEnum
+    member foo
+    member bar
     if ['defined(TEST_IF_ENUM)']
 object q_obj_TestStruct-wrapper
     member data: TestStruct optional=False
-enum TestIfUnionKind ['foo']
+enum TestIfUnionKind
+    member foo
     if ['defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)']
 object TestIfUnion
     member type: TestIfUnionKind optional=False
diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
index 27f776693e..641a18f06d 100644
--- a/tests/qapi-schema/test-qapi.py
+++ b/tests/qapi-schema/test-qapi.py
@@ -24,9 +24,11 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
         print('include %s' % name)
 
     def visit_enum_type(self, name, info, ifcond, members, prefix):
-        print('enum %s %s' % (name, [m.name for m in members]))
+        print('enum %s' % name)
         if prefix:
             print('    prefix %s' % prefix)
+        for m in members:
+            print('    member %s' % m.name)
         self._print_if(ifcond)
 
     def visit_object_type(self, name, info, ifcond, base, members, variants):
-- 
2.20.0.rc1

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

* [Qemu-devel] [PATCH for-4.0 v7 06/27] qapi: factor out checking for keys
  2018-12-08 11:15 [Qemu-devel] [PATCH for-4.0 v7 00/27] Hi, Marc-André Lureau
                   ` (4 preceding siblings ...)
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 05/27] tests: print enum type members more like object type members Marc-André Lureau
@ 2018-12-08 11:15 ` Marc-André Lureau
  2018-12-10  9:57   ` Markus Armbruster
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 07/27] qapi: improve reporting of unknown or missing keys Marc-André Lureau
                   ` (22 subsequent siblings)
  28 siblings, 1 reply; 69+ messages in thread
From: Marc-André Lureau @ 2018-12-08 11:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Marc-André Lureau, Michael Roth

Introduce a new helper function to check if the given keys are known,
and if mandatory keys are present. The function will be reused in
other places in the following code changes.

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

diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index 1fa2f79990..18f5872808 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -873,6 +873,17 @@ def check_struct(expr, info):
                allow_metas=['struct'])
 
 
+def check_known_keys(info, source, keys, required, optional):
+    for key in keys:
+        if key not in required and key not in optional:
+            raise QAPISemError(info, "Unknown key '%s' in %s" % (key, source))
+
+    for key in required:
+        if key not in keys:
+            raise QAPISemError(info, "Key '%s' is missing from %s"
+                               % (key, source))
+
+
 def check_keys(expr_elem, meta, required, optional=[]):
     expr = expr_elem['expr']
     info = expr_elem['info']
@@ -880,10 +891,9 @@ def check_keys(expr_elem, meta, required, optional=[]):
     if not isinstance(name, str):
         raise QAPISemError(info, "'%s' key must have a string value" % meta)
     required = required + [meta]
+    source = "%s '%s'" % (meta, name)
+    check_known_keys(info, source, expr.keys(), required, optional)
     for (key, value) in expr.items():
-        if key not in required and key not in optional:
-            raise QAPISemError(info, "Unknown key '%s' in %s '%s'"
-                               % (key, meta, name))
         if key in ['gen', 'success-response'] and value is not False:
             raise QAPISemError(info,
                                "'%s' of %s '%s' should only use false value"
@@ -895,10 +905,6 @@ def check_keys(expr_elem, meta, required, optional=[]):
                                % (key, meta, name))
         if key == 'if':
             check_if(expr, info)
-    for key in required:
-        if key not in expr:
-            raise QAPISemError(info, "Key '%s' is missing from %s '%s'"
-                               % (key, meta, name))
 
 
 def check_exprs(exprs):
-- 
2.20.0.rc1

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

* [Qemu-devel] [PATCH for-4.0 v7 07/27] qapi: improve reporting of unknown or missing keys
  2018-12-08 11:15 [Qemu-devel] [PATCH for-4.0 v7 00/27] Hi, Marc-André Lureau
                   ` (5 preceding siblings ...)
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 06/27] qapi: factor out checking for keys Marc-André Lureau
@ 2018-12-08 11:15 ` Marc-André Lureau
  2018-12-10 10:03   ` Markus Armbruster
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 08/27] qapi: add a dictionary form with 'name' key for enum members Marc-André Lureau
                   ` (21 subsequent siblings)
  28 siblings, 1 reply; 69+ messages in thread
From: Marc-André Lureau @ 2018-12-08 11:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Marc-André Lureau, Michael Roth

Report the set of missing or unknown keys. And give a hint about the
accepted keys.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 scripts/qapi/common.py                  | 23 +++++++++++++++--------
 tests/qapi-schema/alternate-base.err    |  1 +
 tests/qapi-schema/double-type.err       |  1 +
 tests/qapi-schema/unknown-expr-key.err  |  3 ++-
 tests/qapi-schema/unknown-expr-key.json |  2 +-
 5 files changed, 20 insertions(+), 10 deletions(-)

diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index 18f5872808..f205805751 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -874,14 +874,21 @@ def check_struct(expr, info):
 
 
 def check_known_keys(info, source, keys, required, optional):
-    for key in keys:
-        if key not in required and key not in optional:
-            raise QAPISemError(info, "Unknown key '%s' in %s" % (key, source))
-
-    for key in required:
-        if key not in keys:
-            raise QAPISemError(info, "Key '%s' is missing from %s"
-                               % (key, source))
+
+    def pprint(elems):
+        return ', '.join("'" + e + "'" for e in sorted(elems))
+
+    missing = set(required) - set(keys)
+    if missing:
+        raise QAPISemError(info, "Key%s %s %s missing from %s"
+                           % ('s' if len(missing) > 1 else '', pprint(missing),
+                              'are' if len(missing) > 1 else 'is', source))
+    allowed = set(required + optional)
+    unknown = set(keys) - allowed
+    if unknown:
+        raise QAPISemError(info, "Unknown key%s %s in %s\nValid keys are %s."
+                           % ('s' if len(unknown) > 1 else '', pprint(unknown),
+                              source, pprint(allowed)))
 
 
 def check_keys(expr_elem, meta, required, optional=[]):
diff --git a/tests/qapi-schema/alternate-base.err b/tests/qapi-schema/alternate-base.err
index 30d8a34373..ebe05bc898 100644
--- a/tests/qapi-schema/alternate-base.err
+++ b/tests/qapi-schema/alternate-base.err
@@ -1 +1,2 @@
 tests/qapi-schema/alternate-base.json:4: Unknown key 'base' in alternate 'Alt'
+Valid keys are 'alternate', 'data', 'if'.
diff --git a/tests/qapi-schema/double-type.err b/tests/qapi-schema/double-type.err
index f9613c6d6b..799193dba1 100644
--- a/tests/qapi-schema/double-type.err
+++ b/tests/qapi-schema/double-type.err
@@ -1 +1,2 @@
 tests/qapi-schema/double-type.json:2: Unknown key 'command' in struct 'bar'
+Valid keys are 'base', 'data', 'if', 'struct'.
diff --git a/tests/qapi-schema/unknown-expr-key.err b/tests/qapi-schema/unknown-expr-key.err
index 12f5ed5b43..6ff8bb99c5 100644
--- a/tests/qapi-schema/unknown-expr-key.err
+++ b/tests/qapi-schema/unknown-expr-key.err
@@ -1 +1,2 @@
-tests/qapi-schema/unknown-expr-key.json:2: Unknown key 'bogus' in struct 'bar'
+tests/qapi-schema/unknown-expr-key.json:2: Unknown keys 'bogus', 'phony' in struct 'bar'
+Valid keys are 'base', 'data', 'if', 'struct'.
diff --git a/tests/qapi-schema/unknown-expr-key.json b/tests/qapi-schema/unknown-expr-key.json
index 3b2be00cc4..13292d75ed 100644
--- a/tests/qapi-schema/unknown-expr-key.json
+++ b/tests/qapi-schema/unknown-expr-key.json
@@ -1,2 +1,2 @@
 # we reject an expression with unknown top-level keys
-{ 'struct': 'bar', 'data': { 'string': 'str'}, 'bogus': { } }
+{ 'struct': 'bar', 'data': { 'string': 'str'}, 'bogus': { }, 'phony': { } }
-- 
2.20.0.rc1

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

* [Qemu-devel] [PATCH for-4.0 v7 08/27] qapi: add a dictionary form with 'name' key for enum members
  2018-12-08 11:15 [Qemu-devel] [PATCH for-4.0 v7 00/27] Hi, Marc-André Lureau
                   ` (6 preceding siblings ...)
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 07/27] qapi: improve reporting of unknown or missing keys Marc-André Lureau
@ 2018-12-08 11:15 ` Marc-André Lureau
  2018-12-10 15:44   ` Markus Armbruster
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 09/27] qapi: add 'if' to " Marc-André Lureau
                   ` (20 subsequent siblings)
  28 siblings, 1 reply; 69+ messages in thread
From: Marc-André Lureau @ 2018-12-08 11:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Marc-André Lureau, Michael Roth

Desugar the enum NAME form to { 'name': NAME }. This will allow to add
new enum members, such as 'if' in the following patch.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 scripts/qapi/common.py                        | 49 ++++++++++++++++---
 tests/Makefile.include                        |  3 +-
 tests/qapi-schema/enum-bad-member.err         |  1 +
 ...-dict-member.exit => enum-bad-member.exit} |  0
 tests/qapi-schema/enum-bad-member.json        |  2 +
 ...um-dict-member.out => enum-bad-member.out} |  0
 .../qapi-schema/enum-dict-member-unknown.err  |  2 +
 .../qapi-schema/enum-dict-member-unknown.exit |  1 +
 .../qapi-schema/enum-dict-member-unknown.json |  2 +
 .../qapi-schema/enum-dict-member-unknown.out  |  0
 tests/qapi-schema/enum-dict-member.err        |  1 -
 tests/qapi-schema/enum-dict-member.json       |  2 -
 12 files changed, 52 insertions(+), 11 deletions(-)
 create mode 100644 tests/qapi-schema/enum-bad-member.err
 rename tests/qapi-schema/{enum-dict-member.exit => enum-bad-member.exit} (100%)
 create mode 100644 tests/qapi-schema/enum-bad-member.json
 rename tests/qapi-schema/{enum-dict-member.out => enum-bad-member.out} (100%)
 create mode 100644 tests/qapi-schema/enum-dict-member-unknown.err
 create mode 100644 tests/qapi-schema/enum-dict-member-unknown.exit
 create mode 100644 tests/qapi-schema/enum-dict-member-unknown.json
 create mode 100644 tests/qapi-schema/enum-dict-member-unknown.out
 delete mode 100644 tests/qapi-schema/enum-dict-member.err
 delete mode 100644 tests/qapi-schema/enum-dict-member.json

diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index f205805751..610840d2e5 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -740,6 +740,10 @@ def check_event(expr, info):
                allow_metas=meta)
 
 
+def enum_get_names(expr):
+    return [e['name'] if isinstance(e, dict) else e for e in expr['data']]
+
+
 def check_union(expr, info):
     name = expr['union']
     base = expr.get('base')
@@ -799,7 +803,7 @@ def check_union(expr, info):
         # If the discriminator names an enum type, then all members
         # of 'data' must also be members of the enum type.
         if enum_define:
-            if key not in enum_define['data']:
+            if key not in enum_get_names(enum_define):
                 raise QAPISemError(info,
                                    "Discriminator value '%s' is not found in "
                                    "enum '%s'"
@@ -831,7 +835,7 @@ def check_alternate(expr, info):
         if qtype == 'QTYPE_QSTRING':
             enum_expr = enum_types.get(value)
             if enum_expr:
-                for v in enum_expr['data']:
+                for v in enum_get_names(enum_expr):
                     if v in ['on', 'off']:
                         conflicting.add('QTYPE_QBOOL')
                     if re.match(r'[-+0-9.]', v): # lazy, could be tightened
@@ -847,18 +851,32 @@ def check_alternate(expr, info):
             types_seen[qt] = key
 
 
+def normalize_enum(expr):
+    members = expr['data']
+
+    # translate short member form to dict form
+    expr['data'] = [m if isinstance(m, dict) else {'name': m} for m in members]
+
+
 def check_enum(expr, info):
     name = expr['enum']
-    members = expr.get('data')
+    members = expr['data']
     prefix = expr.get('prefix')
 
-    if not isinstance(members, list):
-        raise QAPISemError(info,
-                           "Enum '%s' requires an array for 'data'" % name)
     if prefix is not None and not isinstance(prefix, str):
         raise QAPISemError(info,
                            "Enum '%s' requires a string for 'prefix'" % name)
+
+    if not isinstance(members, list):
+        raise QAPISemError(info,
+                           "Enum '%s' requires an array for 'data'" % name)
+
     for member in members:
+        if isinstance(member, dict):
+            source = "dictionary member of enum '%s'" % name
+            check_known_keys(info, source, member, ['name'], [])
+            member = member['name']
+
         check_name(info, "Member of enum '%s'" % name, member,
                    enum_member=True)
 
@@ -1011,6 +1029,15 @@ def check_exprs(exprs):
     return exprs
 
 
+def normalize_exprs(exprs):
+    for expr_elem in exprs:
+        expr = expr_elem['expr']
+        if 'enum' in expr:
+            normalize_enum(expr)
+
+    return exprs
+
+
 #
 # Schema compiler frontend
 #
@@ -1567,6 +1594,7 @@ class QAPISchema(object):
             f = open(fname, 'r')
         parser = QAPISchemaParser(f)
         exprs = check_exprs(parser.exprs)
+        exprs = normalize_exprs(exprs)
         self.docs = parser.docs
         self._entity_list = []
         self._entity_dict = {}
@@ -1640,7 +1668,14 @@ class QAPISchema(object):
                                             qtype_values, 'QTYPE'))
 
     def _make_enum_members(self, values):
-        return [QAPISchemaMember(v) for v in values]
+        enum = []
+        for v in values:
+            if isinstance(v, dict):
+                name = v['name']
+            else:
+                name = v
+            enum.append(QAPISchemaMember(name))
+        return enum
 
     def _make_implicit_enum_type(self, name, info, ifcond, values):
         # See also QAPISchemaObjectTypeMember._pretty_owner()
diff --git a/tests/Makefile.include b/tests/Makefile.include
index fb0b449c02..2e894c1037 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -379,10 +379,11 @@ qapi-schema += double-data.json
 qapi-schema += double-type.json
 qapi-schema += duplicate-key.json
 qapi-schema += empty.json
+qapi-schema += enum-bad-member.json
 qapi-schema += enum-bad-name.json
 qapi-schema += enum-bad-prefix.json
 qapi-schema += enum-clash-member.json
-qapi-schema += enum-dict-member.json
+qapi-schema += enum-dict-member-unknown.json
 qapi-schema += enum-int-member.json
 qapi-schema += enum-member-case.json
 qapi-schema += enum-missing-data.json
diff --git a/tests/qapi-schema/enum-bad-member.err b/tests/qapi-schema/enum-bad-member.err
new file mode 100644
index 0000000000..211db9e6fc
--- /dev/null
+++ b/tests/qapi-schema/enum-bad-member.err
@@ -0,0 +1 @@
+tests/qapi-schema/enum-bad-member.json:2: Member of enum 'MyEnum' requires a string name
diff --git a/tests/qapi-schema/enum-dict-member.exit b/tests/qapi-schema/enum-bad-member.exit
similarity index 100%
rename from tests/qapi-schema/enum-dict-member.exit
rename to tests/qapi-schema/enum-bad-member.exit
diff --git a/tests/qapi-schema/enum-bad-member.json b/tests/qapi-schema/enum-bad-member.json
new file mode 100644
index 0000000000..98da6828b4
--- /dev/null
+++ b/tests/qapi-schema/enum-bad-member.json
@@ -0,0 +1,2 @@
+# we reject any enum member that is not a string
+{ 'enum': 'MyEnum', 'data': [ [ ] ] }
diff --git a/tests/qapi-schema/enum-dict-member.out b/tests/qapi-schema/enum-bad-member.out
similarity index 100%
rename from tests/qapi-schema/enum-dict-member.out
rename to tests/qapi-schema/enum-bad-member.out
diff --git a/tests/qapi-schema/enum-dict-member-unknown.err b/tests/qapi-schema/enum-dict-member-unknown.err
new file mode 100644
index 0000000000..76bd0471db
--- /dev/null
+++ b/tests/qapi-schema/enum-dict-member-unknown.err
@@ -0,0 +1,2 @@
+tests/qapi-schema/enum-dict-member-unknown.json:2: Unknown key 'bad-key' in dictionary member of enum 'MyEnum'
+Valid keys are 'name'.
diff --git a/tests/qapi-schema/enum-dict-member-unknown.exit b/tests/qapi-schema/enum-dict-member-unknown.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/enum-dict-member-unknown.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/enum-dict-member-unknown.json b/tests/qapi-schema/enum-dict-member-unknown.json
new file mode 100644
index 0000000000..6664c59201
--- /dev/null
+++ b/tests/qapi-schema/enum-dict-member-unknown.json
@@ -0,0 +1,2 @@
+# we reject any enum member that is not a string or a dict with 'name'
+{ 'enum': 'MyEnum', 'data': [ { 'name': 'foo', 'bad-key': 'str' } ] }
diff --git a/tests/qapi-schema/enum-dict-member-unknown.out b/tests/qapi-schema/enum-dict-member-unknown.out
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tests/qapi-schema/enum-dict-member.err b/tests/qapi-schema/enum-dict-member.err
deleted file mode 100644
index 8ca146ea59..0000000000
--- a/tests/qapi-schema/enum-dict-member.err
+++ /dev/null
@@ -1 +0,0 @@
-tests/qapi-schema/enum-dict-member.json:2: Member of enum 'MyEnum' requires a string name
diff --git a/tests/qapi-schema/enum-dict-member.json b/tests/qapi-schema/enum-dict-member.json
deleted file mode 100644
index 79672e0f09..0000000000
--- a/tests/qapi-schema/enum-dict-member.json
+++ /dev/null
@@ -1,2 +0,0 @@
-# we reject any enum member that is not a string
-{ 'enum': 'MyEnum', 'data': [ { 'value': 'str' } ] }
-- 
2.20.0.rc1

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

* [Qemu-devel] [PATCH for-4.0 v7 09/27] qapi: add 'if' to enum members
  2018-12-08 11:15 [Qemu-devel] [PATCH for-4.0 v7 00/27] Hi, Marc-André Lureau
                   ` (7 preceding siblings ...)
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 08/27] qapi: add a dictionary form with 'name' key for enum members Marc-André Lureau
@ 2018-12-08 11:15 ` Marc-André Lureau
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 10/27] qapi-events: add 'if' condition to implicit event enum Marc-André Lureau
                   ` (19 subsequent siblings)
  28 siblings, 0 replies; 69+ messages in thread
From: Marc-André Lureau @ 2018-12-08 11:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Marc-André Lureau, Michael Roth

QAPISchemaMember gains .ifcond for enum members: inherited classes,
such as QAPISchemaObjectTypeMember, will thus have an ifcond member
after this (those different types will also use the .ifcond to store
the condition and generate conditional code in the following patches).

The generated code remains unconditional for now. Later patches
generate the conditionals.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
---
 scripts/qapi/common.py                         | 10 +++++++---
 docs/devel/qapi-code-gen.txt                   |  9 +++++++++
 tests/Makefile.include                         |  1 +
 tests/qapi-schema/enum-dict-member-unknown.err |  2 +-
 tests/qapi-schema/enum-if-invalid.err          |  1 +
 tests/qapi-schema/enum-if-invalid.exit         |  1 +
 tests/qapi-schema/enum-if-invalid.json         |  3 +++
 tests/qapi-schema/enum-if-invalid.out          |  0
 tests/qapi-schema/qapi-schema-test.json        |  5 +++--
 tests/qapi-schema/qapi-schema-test.out         |  2 ++
 tests/qapi-schema/test-qapi.py                 |  1 +
 11 files changed, 29 insertions(+), 6 deletions(-)
 create mode 100644 tests/qapi-schema/enum-if-invalid.err
 create mode 100644 tests/qapi-schema/enum-if-invalid.exit
 create mode 100644 tests/qapi-schema/enum-if-invalid.json
 create mode 100644 tests/qapi-schema/enum-if-invalid.out

diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index 610840d2e5..fdc0fd69ef 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -874,7 +874,8 @@ def check_enum(expr, info):
     for member in members:
         if isinstance(member, dict):
             source = "dictionary member of enum '%s'" % name
-            check_known_keys(info, source, member, ['name'], [])
+            check_known_keys(info, source, member, ['name'], ['if'])
+            check_if(member, info)
             member = member['name']
 
         check_name(info, "Member of enum '%s'" % name, member,
@@ -1358,9 +1359,10 @@ class QAPISchemaObjectType(QAPISchemaType):
 class QAPISchemaMember(object):
     role = 'member'
 
-    def __init__(self, name):
+    def __init__(self, name, ifcond=None):
         assert isinstance(name, str)
         self.name = name
+        self.ifcond = listify_cond(ifcond)
         self.owner = None
 
     def set_owner(self, name):
@@ -1672,9 +1674,11 @@ class QAPISchema(object):
         for v in values:
             if isinstance(v, dict):
                 name = v['name']
+                ifcond = v.get('if')
             else:
                 name = v
-            enum.append(QAPISchemaMember(name))
+                ifcond = None
+            enum.append(QAPISchemaMember(name, ifcond))
         return enum
 
     def _make_implicit_enum_type(self, name, info, ifcond, values):
diff --git a/docs/devel/qapi-code-gen.txt b/docs/devel/qapi-code-gen.txt
index 53eaf01f34..08c5ef97ff 100644
--- a/docs/devel/qapi-code-gen.txt
+++ b/docs/devel/qapi-code-gen.txt
@@ -752,6 +752,15 @@ gets its generated code guarded like this:
  #endif /* defined(HAVE_BAR) */
  #endif /* defined(CONFIG_FOO) */
 
+An enum value can be replaced by a dictionary with a 'name' and a 'if'
+key.
+
+Example: a conditional 'bar' enum member.
+
+{ 'enum': 'IfEnum', 'data':
+  [ 'foo',
+    { 'name' : 'bar', 'if': 'defined(IFCOND)' } ] }
+
 Please note that you are responsible to ensure that the C code will
 compile with an arbitrary combination of conditions, since the
 generators are unable to check it at this point.
diff --git a/tests/Makefile.include b/tests/Makefile.include
index 2e894c1037..3c9eea27fd 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -384,6 +384,7 @@ qapi-schema += enum-bad-name.json
 qapi-schema += enum-bad-prefix.json
 qapi-schema += enum-clash-member.json
 qapi-schema += enum-dict-member-unknown.json
+qapi-schema += enum-if-invalid.json
 qapi-schema += enum-int-member.json
 qapi-schema += enum-member-case.json
 qapi-schema += enum-missing-data.json
diff --git a/tests/qapi-schema/enum-dict-member-unknown.err b/tests/qapi-schema/enum-dict-member-unknown.err
index 76bd0471db..2aae618be0 100644
--- a/tests/qapi-schema/enum-dict-member-unknown.err
+++ b/tests/qapi-schema/enum-dict-member-unknown.err
@@ -1,2 +1,2 @@
 tests/qapi-schema/enum-dict-member-unknown.json:2: Unknown key 'bad-key' in dictionary member of enum 'MyEnum'
-Valid keys are 'name'.
+Valid keys are 'if', 'name'.
diff --git a/tests/qapi-schema/enum-if-invalid.err b/tests/qapi-schema/enum-if-invalid.err
new file mode 100644
index 0000000000..54c3cf887b
--- /dev/null
+++ b/tests/qapi-schema/enum-if-invalid.err
@@ -0,0 +1 @@
+tests/qapi-schema/enum-if-invalid.json:2: 'if' condition must be a string or a list of strings
diff --git a/tests/qapi-schema/enum-if-invalid.exit b/tests/qapi-schema/enum-if-invalid.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/enum-if-invalid.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/enum-if-invalid.json b/tests/qapi-schema/enum-if-invalid.json
new file mode 100644
index 0000000000..60bd0ef1d7
--- /dev/null
+++ b/tests/qapi-schema/enum-if-invalid.json
@@ -0,0 +1,3 @@
+# check invalid 'if' type
+{ 'enum': 'TestIfEnum', 'data':
+  [ 'foo', { 'name' : 'bar', 'if': { 'val': 'foo' } } ] }
diff --git a/tests/qapi-schema/enum-if-invalid.out b/tests/qapi-schema/enum-if-invalid.out
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index fb03163430..22d9044a89 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -204,7 +204,8 @@
 { 'struct': 'TestIfStruct', 'data': { 'foo': 'int' },
   'if': 'defined(TEST_IF_STRUCT)' }
 
-{ 'enum': 'TestIfEnum', 'data': [ 'foo', 'bar' ],
+{ 'enum': 'TestIfEnum', 'data':
+  [ 'foo', { 'name' : 'bar', 'if': 'defined(TEST_IF_ENUM_BAR)' } ],
   'if': 'defined(TEST_IF_ENUM)' }
 
 { 'union': 'TestIfUnion', 'data': { 'foo': 'TestStruct' },
@@ -213,7 +214,7 @@
 { 'alternate': 'TestIfAlternate', 'data': { 'foo': 'int', 'bar': 'TestStruct' },
   'if': 'defined(TEST_IF_ALT) && defined(TEST_IF_STRUCT)' }
 
-{ 'command': 'TestIfCmd', 'data': { 'foo': 'TestIfStruct' },
+{ 'command': 'TestIfCmd', 'data': { 'foo': 'TestIfStruct', 'bar': 'TestIfEnum' },
   'returns': 'UserDefThree',
   'if': ['defined(TEST_IF_CMD)', 'defined(TEST_IF_STRUCT)'] }
 
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index 39cd2b0fc3..130199fe8f 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -272,6 +272,7 @@ object TestIfStruct
 enum TestIfEnum
     member foo
     member bar
+        if ['defined(TEST_IF_ENUM_BAR)']
     if ['defined(TEST_IF_ENUM)']
 object q_obj_TestStruct-wrapper
     member data: TestStruct optional=False
@@ -290,6 +291,7 @@ alternate TestIfAlternate
     if ['defined(TEST_IF_ALT) && defined(TEST_IF_STRUCT)']
 object q_obj_TestIfCmd-arg
     member foo: TestIfStruct optional=False
+    member bar: TestIfEnum optional=False
     if ['defined(TEST_IF_CMD)', 'defined(TEST_IF_STRUCT)']
 command TestIfCmd q_obj_TestIfCmd-arg -> UserDefThree
    gen=True success_response=True boxed=False oob=False preconfig=False
diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
index 641a18f06d..aadf252d9d 100644
--- a/tests/qapi-schema/test-qapi.py
+++ b/tests/qapi-schema/test-qapi.py
@@ -29,6 +29,7 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
             print('    prefix %s' % prefix)
         for m in members:
             print('    member %s' % m.name)
+            self._print_if(m.ifcond, indent=8)
         self._print_if(ifcond)
 
     def visit_object_type(self, name, info, ifcond, base, members, variants):
-- 
2.20.0.rc1

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

* [Qemu-devel] [PATCH for-4.0 v7 10/27] qapi-events: add 'if' condition to implicit event enum
  2018-12-08 11:15 [Qemu-devel] [PATCH for-4.0 v7 00/27] Hi, Marc-André Lureau
                   ` (8 preceding siblings ...)
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 09/27] qapi: add 'if' to " Marc-André Lureau
@ 2018-12-08 11:15 ` Marc-André Lureau
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 11/27] qapi: pass long form enum to make_enum_members Marc-André Lureau
                   ` (18 subsequent siblings)
  28 siblings, 0 replies; 69+ messages in thread
From: Marc-André Lureau @ 2018-12-08 11:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Marc-André Lureau, Michael Roth

Add condition to QAPIEvent enum members based on the event 'if'.

The generated code remains unconditional for now. Later patches
generate the conditionals (also there is no additional coverage of
this change in qapi-schema-test.out since the event_names enum is an
implicit type created by qapi/events.py).

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
---
 scripts/qapi/events.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/scripts/qapi/events.py b/scripts/qapi/events.py
index f1b88d8786..37ee5de682 100644
--- a/scripts/qapi/events.py
+++ b/scripts/qapi/events.py
@@ -179,7 +179,7 @@ class QAPISchemaGenEventVisitor(QAPISchemaModularCVisitor):
             self._genh.add(gen_event_send_decl(name, arg_type, boxed))
             self._genc.add(gen_event_send(name, arg_type, boxed,
                                           self._event_enum_name))
-        self._event_enum_members.append(QAPISchemaMember(name))
+        self._event_enum_members.append(QAPISchemaMember(name, ifcond))
 
 
 def gen_events(schema, output_dir, prefix):
-- 
2.20.0.rc1

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

* [Qemu-devel] [PATCH for-4.0 v7 11/27] qapi: pass long form enum to make_enum_members
  2018-12-08 11:15 [Qemu-devel] [PATCH for-4.0 v7 00/27] Hi, Marc-André Lureau
                   ` (9 preceding siblings ...)
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 10/27] qapi-events: add 'if' condition to implicit event enum Marc-André Lureau
@ 2018-12-08 11:15 ` Marc-André Lureau
  2018-12-10 17:04   ` Markus Armbruster
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 12/27] qapi: rename allow_dict to allow_implicit Marc-André Lureau
                   ` (17 subsequent siblings)
  28 siblings, 1 reply; 69+ messages in thread
From: Marc-André Lureau @ 2018-12-08 11:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Marc-André Lureau, Michael Roth

This will allow to get rid of short form handling in a following
patch.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Suggested-by: Markus Armbruster <armbru@redhat.com>
---
 scripts/qapi/common.py | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index fdc0fd69ef..557b413950 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -1663,9 +1663,10 @@ class QAPISchema(object):
         self.the_empty_object_type = QAPISchemaObjectType(
             'q_empty', None, None, None, None, [], None)
         self._def_entity(self.the_empty_object_type)
-        qtype_values = self._make_enum_members(['none', 'qnull', 'qnum',
-                                                'qstring', 'qdict', 'qlist',
-                                                'qbool'])
+
+        qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
+        qtype_values = self._make_enum_members([{'name': n} for n in qtypes])
+
         self._def_entity(QAPISchemaEnumType('QType', None, None, None,
                                             qtype_values, 'QTYPE'))
 
-- 
2.20.0.rc1

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

* [Qemu-devel] [PATCH for-4.0 v7 12/27] qapi: rename allow_dict to allow_implicit
  2018-12-08 11:15 [Qemu-devel] [PATCH for-4.0 v7 00/27] Hi, Marc-André Lureau
                   ` (10 preceding siblings ...)
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 11/27] qapi: pass long form enum to make_enum_members Marc-André Lureau
@ 2018-12-08 11:15 ` Marc-André Lureau
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 13/27] qapi: add a dictionary form for TYPE Marc-André Lureau
                   ` (16 subsequent siblings)
  28 siblings, 0 replies; 69+ messages in thread
From: Marc-André Lureau @ 2018-12-08 11:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Marc-André Lureau, Michael Roth

This makes it a bit clearer what is the intent of the dictionnary for
the check_type() function, since there was some confusion on a
previous iteration of this series.

Suggested-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 scripts/qapi/common.py | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index 557b413950..95e55b3f44 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -661,7 +661,7 @@ def check_if(expr, info):
 
 
 def check_type(info, source, value, allow_array=False,
-               allow_dict=False, allow_optional=False,
+               allow_implicit=False, allow_optional=False,
                allow_metas=[]):
     global all_names
 
@@ -688,7 +688,7 @@ def check_type(info, source, value, allow_array=False,
                                (source, all_names[value], value))
         return
 
-    if not allow_dict:
+    if not allow_implicit:
         raise QAPISemError(info, "%s should be a type name" % source)
 
     if not isinstance(value, OrderedDict):
@@ -718,7 +718,7 @@ def check_command(expr, info):
     if boxed:
         args_meta += ['union', 'alternate']
     check_type(info, "'data' for command '%s'" % name,
-               expr.get('data'), allow_dict=not boxed, allow_optional=True,
+               expr.get('data'), allow_implicit=not boxed, allow_optional=True,
                allow_metas=args_meta)
     returns_meta = ['union', 'struct']
     if name in returns_whitelist:
@@ -736,7 +736,7 @@ def check_event(expr, info):
     if boxed:
         meta += ['union', 'alternate']
     check_type(info, "'data' for event '%s'" % name,
-               expr.get('data'), allow_dict=not boxed, allow_optional=True,
+               expr.get('data'), allow_implicit=not boxed, allow_optional=True,
                allow_metas=meta)
 
 
@@ -764,7 +764,7 @@ def check_union(expr, info):
     else:
         # The object must have a string or dictionary 'base'.
         check_type(info, "'base' for union '%s'" % name,
-                   base, allow_dict=True, allow_optional=True,
+                   base, allow_implicit=True, allow_optional=True,
                    allow_metas=['struct'])
         if not base:
             raise QAPISemError(info, "Flat union '%s' must have a base"
@@ -887,7 +887,7 @@ def check_struct(expr, info):
     members = expr['data']
 
     check_type(info, "'data' for struct '%s'" % name, members,
-               allow_dict=True, allow_optional=True)
+               allow_implicit=True, allow_optional=True)
     check_type(info, "'base' for struct '%s'" % name, expr.get('base'),
                allow_metas=['struct'])
 
-- 
2.20.0.rc1

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

* [Qemu-devel] [PATCH for-4.0 v7 13/27] qapi: add a dictionary form for TYPE
  2018-12-08 11:15 [Qemu-devel] [PATCH for-4.0 v7 00/27] Hi, Marc-André Lureau
                   ` (11 preceding siblings ...)
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 12/27] qapi: rename allow_dict to allow_implicit Marc-André Lureau
@ 2018-12-08 11:15 ` Marc-André Lureau
  2018-12-10 17:24   ` Markus Armbruster
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 14/27] qapi: add 'if' to implicit struct members Marc-André Lureau
                   ` (15 subsequent siblings)
  28 siblings, 1 reply; 69+ messages in thread
From: Marc-André Lureau @ 2018-12-08 11:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Marc-André Lureau, Michael Roth

Wherever a struct/union/alternate/command/event member with NAME: TYPE
form is accepted, desugar it to a NAME: { 'type': TYPE } form.

This will allow to add new member details, such as 'if' in the
following patch to introduce conditionals, or 'default' for default
values etc.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 scripts/qapi/common.py                        | 71 ++++++++++++++-----
 tests/Makefile.include                        |  6 ++
 tests/qapi-schema/alternate-invalid-dict.err  |  1 +
 tests/qapi-schema/alternate-invalid-dict.exit |  1 +
 tests/qapi-schema/alternate-invalid-dict.json |  4 ++
 tests/qapi-schema/alternate-invalid-dict.out  |  0
 .../qapi-schema/event-member-invalid-dict.err |  1 +
 .../event-member-invalid-dict.exit            |  1 +
 .../event-member-invalid-dict.json            |  2 +
 .../qapi-schema/event-member-invalid-dict.out |  0
 tests/qapi-schema/event-nest-struct.json      |  2 +-
 .../flat-union-inline-invalid-dict.err        |  1 +
 .../flat-union-inline-invalid-dict.exit       |  1 +
 .../flat-union-inline-invalid-dict.json       | 11 +++
 .../flat-union-inline-invalid-dict.out        |  0
 tests/qapi-schema/flat-union-inline.json      |  2 +-
 .../nested-struct-data-invalid-dict.err       |  1 +
 .../nested-struct-data-invalid-dict.exit      |  1 +
 .../nested-struct-data-invalid-dict.json      |  3 +
 .../nested-struct-data-invalid-dict.out       |  0
 tests/qapi-schema/nested-struct-data.json     |  2 +-
 tests/qapi-schema/qapi-schema-test.json       | 10 +--
 .../struct-member-invalid-dict.err            |  1 +
 .../struct-member-invalid-dict.exit           |  1 +
 .../struct-member-invalid-dict.json           |  3 +
 .../struct-member-invalid-dict.out            |  0
 .../qapi-schema/union-branch-invalid-dict.err |  1 +
 .../union-branch-invalid-dict.exit            |  1 +
 .../union-branch-invalid-dict.json            |  4 ++
 .../qapi-schema/union-branch-invalid-dict.out |  0
 30 files changed, 106 insertions(+), 26 deletions(-)
 create mode 100644 tests/qapi-schema/alternate-invalid-dict.err
 create mode 100644 tests/qapi-schema/alternate-invalid-dict.exit
 create mode 100644 tests/qapi-schema/alternate-invalid-dict.json
 create mode 100644 tests/qapi-schema/alternate-invalid-dict.out
 create mode 100644 tests/qapi-schema/event-member-invalid-dict.err
 create mode 100644 tests/qapi-schema/event-member-invalid-dict.exit
 create mode 100644 tests/qapi-schema/event-member-invalid-dict.json
 create mode 100644 tests/qapi-schema/event-member-invalid-dict.out
 create mode 100644 tests/qapi-schema/flat-union-inline-invalid-dict.err
 create mode 100644 tests/qapi-schema/flat-union-inline-invalid-dict.exit
 create mode 100644 tests/qapi-schema/flat-union-inline-invalid-dict.json
 create mode 100644 tests/qapi-schema/flat-union-inline-invalid-dict.out
 create mode 100644 tests/qapi-schema/nested-struct-data-invalid-dict.err
 create mode 100644 tests/qapi-schema/nested-struct-data-invalid-dict.exit
 create mode 100644 tests/qapi-schema/nested-struct-data-invalid-dict.json
 create mode 100644 tests/qapi-schema/nested-struct-data-invalid-dict.out
 create mode 100644 tests/qapi-schema/struct-member-invalid-dict.err
 create mode 100644 tests/qapi-schema/struct-member-invalid-dict.exit
 create mode 100644 tests/qapi-schema/struct-member-invalid-dict.json
 create mode 100644 tests/qapi-schema/struct-member-invalid-dict.out
 create mode 100644 tests/qapi-schema/union-branch-invalid-dict.err
 create mode 100644 tests/qapi-schema/union-branch-invalid-dict.exit
 create mode 100644 tests/qapi-schema/union-branch-invalid-dict.json
 create mode 100644 tests/qapi-schema/union-branch-invalid-dict.out

diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index 95e55b3f44..4b3ba53dc7 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -588,11 +588,13 @@ def discriminator_find_enum_define(expr):
     if not base_members:
         return None
 
-    discriminator_type = base_members.get(discriminator)
-    if not discriminator_type:
+    discriminator_value = base_members.get(discriminator)
+    if not discriminator_value:
         return None
 
-    return enum_types.get(discriminator_type)
+    if isinstance(discriminator_value, dict):
+        discriminator_value = discriminator_value['type']
+    return enum_types.get(discriminator_value)
 
 
 # Names must be letters, numbers, -, and _.  They must start with letter,
@@ -660,6 +662,15 @@ def check_if(expr, info):
         check_if_str(ifcond, info)
 
 
+def normalize_members(expr, field):
+    members = expr.get(field)
+    if isinstance(members, OrderedDict):
+        for key, arg in members.items():
+            if isinstance(arg, dict):
+                continue
+            members[key] = {'type': arg}
+
+
 def check_type(info, source, value, allow_array=False,
                allow_implicit=False, allow_optional=False,
                allow_metas=[]):
@@ -704,8 +715,14 @@ def check_type(info, source, value, allow_array=False,
                                % (source, key))
         # Todo: allow dictionaries to represent default values of
         # an optional argument.
-        check_type(info, "Member '%s' of %s" % (key, source), arg,
-                   allow_array=True,
+        if isinstance(arg, dict):
+            check_known_keys(info, "member '%s' of %s" % (key, source),
+                             arg, ['type'], [])
+            typ = arg['type']
+        else:
+            typ = arg
+        check_type(info, "Member '%s' of %s" % (key, source),
+                   typ, allow_array=True,
                    allow_metas=['built-in', 'union', 'alternate', 'struct',
                                 'enum'])
 
@@ -776,13 +793,15 @@ def check_union(expr, info):
         # member of the base struct.
         check_name(info, "Discriminator of flat union '%s'" % name,
                    discriminator)
-        discriminator_type = base_members.get(discriminator)
-        if not discriminator_type:
+        discriminator_value = base_members.get(discriminator)
+        if not discriminator_value:
             raise QAPISemError(info,
                                "Discriminator '%s' is not a member of base "
                                "struct '%s'"
                                % (discriminator, base))
-        enum_define = enum_types.get(discriminator_type)
+        if isinstance(discriminator_value, dict):
+            discriminator_value = discriminator_value['type']
+        enum_define = enum_types.get(discriminator_value)
         allow_metas = ['struct']
         # Do not allow string discriminator
         if not enum_define:
@@ -795,10 +814,16 @@ def check_union(expr, info):
         raise QAPISemError(info, "Union '%s' cannot have empty 'data'" % name)
     for (key, value) in members.items():
         check_name(info, "Member of union '%s'" % name, key)
+        if isinstance(value, dict):
+            check_known_keys(info, "member '%s' of union '%s'" % (key, name),
+                             value, ['type'], [])
+            typ = value['type']
+        else:
+            typ = value
 
         # Each value must name a known type
         check_type(info, "Member '%s' of union '%s'" % (key, name),
-                   value, allow_array=not base, allow_metas=allow_metas)
+                   typ, allow_array=not base, allow_metas=allow_metas)
 
         # If the discriminator names an enum type, then all members
         # of 'data' must also be members of the enum type.
@@ -822,18 +847,24 @@ def check_alternate(expr, info):
                            "in 'data'" % name)
     for (key, value) in members.items():
         check_name(info, "Member of alternate '%s'" % name, key)
+        if isinstance(value, dict):
+            check_known_keys(info,
+                             "member '%s' of alternate '%s'" % (key, name),
+                             value, ['type'], [])
+            typ = value['type']
+        else:
+            typ = value
 
         # Ensure alternates have no type conflicts.
-        check_type(info, "Member '%s' of alternate '%s'" % (key, name),
-                   value,
+        check_type(info, "Member '%s' of alternate '%s'" % (key, name), typ,
                    allow_metas=['built-in', 'union', 'struct', 'enum'])
-        qtype = find_alternate_member_qtype(value)
+        qtype = find_alternate_member_qtype(typ)
         if not qtype:
             raise QAPISemError(info, "Alternate '%s' member '%s' cannot use "
-                               "type '%s'" % (name, key, value))
+                               "type '%s'" % (name, key, typ))
         conflicting = set([qtype])
         if qtype == 'QTYPE_QSTRING':
-            enum_expr = enum_types.get(value)
+            enum_expr = enum_types.get(typ)
             if enum_expr:
                 for v in enum_get_names(enum_expr):
                     if v in ['on', 'off']:
@@ -1035,6 +1066,10 @@ def normalize_exprs(exprs):
         expr = expr_elem['expr']
         if 'enum' in expr:
             normalize_enum(expr)
+        elif 'union' in expr:
+            normalize_members(expr, 'base')
+        if {'union', 'alternate', 'struct', 'command', 'event'} & set(expr):
+            normalize_members(expr, 'data')
 
     return exprs
 
@@ -1738,7 +1773,7 @@ class QAPISchema(object):
         return QAPISchemaObjectTypeMember(name, typ, optional)
 
     def _make_members(self, data, info):
-        return [self._make_member(key, value, info)
+        return [self._make_member(key, value['type'], info)
                 for (key, value) in data.items()]
 
     def _def_struct_type(self, expr, info, doc):
@@ -1774,11 +1809,11 @@ class QAPISchema(object):
                 name, info, doc, ifcond,
                 'base', self._make_members(base, info))
         if tag_name:
-            variants = [self._make_variant(key, value)
+            variants = [self._make_variant(key, value['type'])
                         for (key, value) in data.items()]
             members = []
         else:
-            variants = [self._make_simple_variant(key, value, info)
+            variants = [self._make_simple_variant(key, value['type'], info)
                         for (key, value) in data.items()]
             typ = self._make_implicit_enum_type(name, info, ifcond,
                                                 [v.name for v in variants])
@@ -1794,7 +1829,7 @@ class QAPISchema(object):
         name = expr['alternate']
         data = expr['data']
         ifcond = expr.get('if')
-        variants = [self._make_variant(key, value)
+        variants = [self._make_variant(key, value['type'])
                     for (key, value) in data.items()]
         tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
         self._def_entity(
diff --git a/tests/Makefile.include b/tests/Makefile.include
index 3c9eea27fd..ea5d1e8787 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -318,6 +318,7 @@ qapi-schema += alternate-conflict-string.json
 qapi-schema += alternate-conflict-bool-string.json
 qapi-schema += alternate-conflict-num-string.json
 qapi-schema += alternate-empty.json
+qapi-schema += alternate-invalid-dict.json
 qapi-schema += alternate-nested.json
 qapi-schema += alternate-unknown.json
 qapi-schema += args-alternate.json
@@ -394,6 +395,7 @@ qapi-schema += escape-too-big.json
 qapi-schema += escape-too-short.json
 qapi-schema += event-boxed-empty.json
 qapi-schema += event-case.json
+qapi-schema += event-member-invalid-dict.json
 qapi-schema += event-nest-struct.json
 qapi-schema += flat-union-array-branch.json
 qapi-schema += flat-union-bad-base.json
@@ -403,6 +405,7 @@ qapi-schema += flat-union-base-union.json
 qapi-schema += flat-union-clash-member.json
 qapi-schema += flat-union-empty.json
 qapi-schema += flat-union-inline.json
+qapi-schema += flat-union-inline-invalid-dict.json
 qapi-schema += flat-union-int-branch.json
 qapi-schema += flat-union-invalid-branch-key.json
 qapi-schema += flat-union-invalid-discriminator.json
@@ -430,6 +433,7 @@ qapi-schema += missing-comma-list.json
 qapi-schema += missing-comma-object.json
 qapi-schema += missing-type.json
 qapi-schema += nested-struct-data.json
+qapi-schema += nested-struct-data-invalid-dict.json
 qapi-schema += non-objects.json
 qapi-schema += oob-test.json
 qapi-schema += allow-preconfig-test.json
@@ -460,6 +464,7 @@ qapi-schema += returns-whitelist.json
 qapi-schema += struct-base-clash-deep.json
 qapi-schema += struct-base-clash.json
 qapi-schema += struct-data-invalid.json
+qapi-schema += struct-member-invalid-dict.json
 qapi-schema += struct-member-invalid.json
 qapi-schema += trailing-comma-list.json
 qapi-schema += trailing-comma-object.json
@@ -471,6 +476,7 @@ qapi-schema += unicode-str.json
 qapi-schema += union-base-empty.json
 qapi-schema += union-base-no-discriminator.json
 qapi-schema += union-branch-case.json
+qapi-schema += union-branch-invalid-dict.json
 qapi-schema += union-clash-branches.json
 qapi-schema += union-empty.json
 qapi-schema += union-invalid-base.json
diff --git a/tests/qapi-schema/alternate-invalid-dict.err b/tests/qapi-schema/alternate-invalid-dict.err
new file mode 100644
index 0000000000..631d46628e
--- /dev/null
+++ b/tests/qapi-schema/alternate-invalid-dict.err
@@ -0,0 +1 @@
+tests/qapi-schema/alternate-invalid-dict.json:2: Key 'type' is missing from member 'two' of alternate 'Alt'
diff --git a/tests/qapi-schema/alternate-invalid-dict.exit b/tests/qapi-schema/alternate-invalid-dict.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/alternate-invalid-dict.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/alternate-invalid-dict.json b/tests/qapi-schema/alternate-invalid-dict.json
new file mode 100644
index 0000000000..8e0b2ac287
--- /dev/null
+++ b/tests/qapi-schema/alternate-invalid-dict.json
@@ -0,0 +1,4 @@
+# exploded member form must have a 'type'
+{ 'alternate': 'Alt',
+  'data': { 'one': 'str',
+            'two': { 'if': 'foo' } } }
diff --git a/tests/qapi-schema/alternate-invalid-dict.out b/tests/qapi-schema/alternate-invalid-dict.out
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tests/qapi-schema/event-member-invalid-dict.err b/tests/qapi-schema/event-member-invalid-dict.err
new file mode 100644
index 0000000000..1a57fa29b0
--- /dev/null
+++ b/tests/qapi-schema/event-member-invalid-dict.err
@@ -0,0 +1 @@
+tests/qapi-schema/event-member-invalid-dict.json:1: Key 'type' is missing from member 'a' of 'data' for event 'EVENT_A'
diff --git a/tests/qapi-schema/event-member-invalid-dict.exit b/tests/qapi-schema/event-member-invalid-dict.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/event-member-invalid-dict.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/event-member-invalid-dict.json b/tests/qapi-schema/event-member-invalid-dict.json
new file mode 100644
index 0000000000..ee6f3ecb6f
--- /dev/null
+++ b/tests/qapi-schema/event-member-invalid-dict.json
@@ -0,0 +1,2 @@
+{ 'event': 'EVENT_A',
+  'data': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' } }
diff --git a/tests/qapi-schema/event-member-invalid-dict.out b/tests/qapi-schema/event-member-invalid-dict.out
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tests/qapi-schema/event-nest-struct.json b/tests/qapi-schema/event-nest-struct.json
index ee6f3ecb6f..355ddaeff1 100644
--- a/tests/qapi-schema/event-nest-struct.json
+++ b/tests/qapi-schema/event-nest-struct.json
@@ -1,2 +1,2 @@
 { 'event': 'EVENT_A',
-  'data': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' } }
+  'data': { 'a' : { 'type' : { 'integer': 'int' } }, 'b' : 'str' } }
diff --git a/tests/qapi-schema/flat-union-inline-invalid-dict.err b/tests/qapi-schema/flat-union-inline-invalid-dict.err
new file mode 100644
index 0000000000..9c4c45b7f0
--- /dev/null
+++ b/tests/qapi-schema/flat-union-inline-invalid-dict.err
@@ -0,0 +1 @@
+tests/qapi-schema/flat-union-inline-invalid-dict.json:7: Key 'type' is missing from member 'value1' of union 'TestUnion'
diff --git a/tests/qapi-schema/flat-union-inline-invalid-dict.exit b/tests/qapi-schema/flat-union-inline-invalid-dict.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/flat-union-inline-invalid-dict.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/flat-union-inline-invalid-dict.json b/tests/qapi-schema/flat-union-inline-invalid-dict.json
new file mode 100644
index 0000000000..62c7cda617
--- /dev/null
+++ b/tests/qapi-schema/flat-union-inline-invalid-dict.json
@@ -0,0 +1,11 @@
+# we require branches to be a struct name
+# TODO: should we allow anonymous inline branch types?
+{ 'enum': 'TestEnum',
+  'data': [ 'value1', 'value2' ] }
+{ 'struct': 'Base',
+  'data': { 'enum1': 'TestEnum', 'kind': 'str' } }
+{ 'union': 'TestUnion',
+  'base': 'Base',
+  'discriminator': 'enum1',
+  'data': { 'value1': { 'string': 'str' },
+            'value2': { 'integer': 'int' } } }
diff --git a/tests/qapi-schema/flat-union-inline-invalid-dict.out b/tests/qapi-schema/flat-union-inline-invalid-dict.out
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tests/qapi-schema/flat-union-inline.json b/tests/qapi-schema/flat-union-inline.json
index 62c7cda617..a9b3ce3f0d 100644
--- a/tests/qapi-schema/flat-union-inline.json
+++ b/tests/qapi-schema/flat-union-inline.json
@@ -7,5 +7,5 @@
 { 'union': 'TestUnion',
   'base': 'Base',
   'discriminator': 'enum1',
-  'data': { 'value1': { 'string': 'str' },
+  'data': { 'value1': { 'type': {} },
             'value2': { 'integer': 'int' } } }
diff --git a/tests/qapi-schema/nested-struct-data-invalid-dict.err b/tests/qapi-schema/nested-struct-data-invalid-dict.err
new file mode 100644
index 0000000000..5bd364e8d9
--- /dev/null
+++ b/tests/qapi-schema/nested-struct-data-invalid-dict.err
@@ -0,0 +1 @@
+tests/qapi-schema/nested-struct-data-invalid-dict.json:2: Key 'type' is missing from member 'a' of 'data' for command 'foo'
diff --git a/tests/qapi-schema/nested-struct-data-invalid-dict.exit b/tests/qapi-schema/nested-struct-data-invalid-dict.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/nested-struct-data-invalid-dict.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/nested-struct-data-invalid-dict.json b/tests/qapi-schema/nested-struct-data-invalid-dict.json
new file mode 100644
index 0000000000..efbe773ded
--- /dev/null
+++ b/tests/qapi-schema/nested-struct-data-invalid-dict.json
@@ -0,0 +1,3 @@
+# inline subtypes collide with our desired future use of defaults
+{ 'command': 'foo',
+  'data': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' } }
diff --git a/tests/qapi-schema/nested-struct-data-invalid-dict.out b/tests/qapi-schema/nested-struct-data-invalid-dict.out
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tests/qapi-schema/nested-struct-data.json b/tests/qapi-schema/nested-struct-data.json
index efbe773ded..5b8a40cca3 100644
--- a/tests/qapi-schema/nested-struct-data.json
+++ b/tests/qapi-schema/nested-struct-data.json
@@ -1,3 +1,3 @@
 # inline subtypes collide with our desired future use of defaults
 { 'command': 'foo',
-  'data': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' } }
+  'data': { 'a' : { 'type': {} }, 'b' : 'str' } }
diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index 22d9044a89..94570154c9 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -11,7 +11,7 @@
         'guest-sync' ] } }
 
 { 'struct': 'TestStruct',
-  'data': { 'integer': 'int', 'boolean': 'bool', 'string': 'str' } }
+  'data': { 'integer': {'type': 'int'}, 'boolean': 'bool', 'string': 'str' } }
 
 # for testing enums
 { 'struct': 'NestedEnumsOne',
@@ -77,7 +77,7 @@
 { 'union': 'UserDefFlatUnion',
   'base': 'UserDefUnionBase',   # intentional forward reference
   'discriminator': 'enum1',
-  'data': { 'value1' : 'UserDefA',
+  'data': { 'value1' : {'type': 'UserDefA'},
             'value2' : 'UserDefB',
             'value3' : 'UserDefB'
             # 'value4' defaults to empty
@@ -98,7 +98,7 @@
 { 'struct': 'WrapAlternate',
   'data': { 'alt': 'UserDefAlternate' } }
 { 'alternate': 'UserDefAlternate',
-  'data': { 'udfu': 'UserDefFlatUnion', 'e': 'EnumOne', 'i': 'int',
+  'data': { 'udfu': {'type': 'UserDefFlatUnion'}, 'e': 'EnumOne', 'i': 'int',
             'n': 'null' } }
 
 { 'struct': 'UserDefC',
@@ -134,7 +134,7 @@
 { 'command': 'user_def_cmd', 'data': {} }
 { 'command': 'user_def_cmd1', 'data': {'ud1a': 'UserDefOne'} }
 { 'command': 'user_def_cmd2',
-  'data': {'ud1a': 'UserDefOne', '*ud1b': 'UserDefOne'},
+  'data': {'ud1a': {'type': 'UserDefOne'}, '*ud1b': 'UserDefOne'},
   'returns': 'UserDefTwo' }
 
 { 'command': 'cmd-success-response', 'data': {}, 'success-response': false }
@@ -166,7 +166,7 @@
 
 # testing event
 { 'struct': 'EventStructOne',
-  'data': { 'struct1': 'UserDefOne', 'string': 'str', '*enum2': 'EnumOne' } }
+  'data': { 'struct1': {'type': 'UserDefOne'}, 'string': 'str', '*enum2': 'EnumOne' } }
 
 { 'event': 'EVENT_A' }
 { 'event': 'EVENT_B',
diff --git a/tests/qapi-schema/struct-member-invalid-dict.err b/tests/qapi-schema/struct-member-invalid-dict.err
new file mode 100644
index 0000000000..6a765bc668
--- /dev/null
+++ b/tests/qapi-schema/struct-member-invalid-dict.err
@@ -0,0 +1 @@
+tests/qapi-schema/struct-member-invalid-dict.json:2: Key 'type' is missing from member '*a' of 'data' for struct 'foo'
diff --git a/tests/qapi-schema/struct-member-invalid-dict.exit b/tests/qapi-schema/struct-member-invalid-dict.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/struct-member-invalid-dict.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/struct-member-invalid-dict.json b/tests/qapi-schema/struct-member-invalid-dict.json
new file mode 100644
index 0000000000..9fe0d455a9
--- /dev/null
+++ b/tests/qapi-schema/struct-member-invalid-dict.json
@@ -0,0 +1,3 @@
+# Long form of member must have a value member 'type'
+{ 'struct': 'foo',
+  'data': { '*a': { 'case': 'foo' } } }
diff --git a/tests/qapi-schema/struct-member-invalid-dict.out b/tests/qapi-schema/struct-member-invalid-dict.out
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tests/qapi-schema/union-branch-invalid-dict.err b/tests/qapi-schema/union-branch-invalid-dict.err
new file mode 100644
index 0000000000..89f9b36791
--- /dev/null
+++ b/tests/qapi-schema/union-branch-invalid-dict.err
@@ -0,0 +1 @@
+tests/qapi-schema/union-branch-invalid-dict.json:2: Key 'type' is missing from member 'integer' of union 'UnionInvalidBranch'
diff --git a/tests/qapi-schema/union-branch-invalid-dict.exit b/tests/qapi-schema/union-branch-invalid-dict.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/union-branch-invalid-dict.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/union-branch-invalid-dict.json b/tests/qapi-schema/union-branch-invalid-dict.json
new file mode 100644
index 0000000000..9778598dbd
--- /dev/null
+++ b/tests/qapi-schema/union-branch-invalid-dict.json
@@ -0,0 +1,4 @@
+# Long form of member must have a value member 'type'
+{ 'union': 'UnionInvalidBranch',
+  'data': { 'integer': { 'if': 'foo'},
+            's8': 'int8' } }
diff --git a/tests/qapi-schema/union-branch-invalid-dict.out b/tests/qapi-schema/union-branch-invalid-dict.out
new file mode 100644
index 0000000000..e69de29bb2
-- 
2.20.0.rc1

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

* [Qemu-devel] [PATCH for-4.0 v7 14/27] qapi: add 'if' to implicit struct members
  2018-12-08 11:15 [Qemu-devel] [PATCH for-4.0 v7 00/27] Hi, Marc-André Lureau
                   ` (12 preceding siblings ...)
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 13/27] qapi: add a dictionary form for TYPE Marc-André Lureau
@ 2018-12-08 11:15 ` Marc-André Lureau
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 15/27] qapi: add an error in case a discriminator is conditional Marc-André Lureau
                   ` (14 subsequent siblings)
  28 siblings, 0 replies; 69+ messages in thread
From: Marc-André Lureau @ 2018-12-08 11:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Marc-André Lureau, Michael Roth

The generated code is for now *unconditional*. Later patches generate
the conditionals.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
---
 scripts/qapi/common.py                  | 14 +++++++-------
 docs/devel/qapi-code-gen.txt            | 10 ++++++++++
 tests/qapi-schema/qapi-schema-test.json | 12 +++++++++---
 tests/qapi-schema/qapi-schema-test.out  |  5 +++++
 tests/qapi-schema/test-qapi.py          |  1 +
 5 files changed, 32 insertions(+), 10 deletions(-)

diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index 4b3ba53dc7..aec51acb07 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -717,7 +717,7 @@ def check_type(info, source, value, allow_array=False,
         # an optional argument.
         if isinstance(arg, dict):
             check_known_keys(info, "member '%s' of %s" % (key, source),
-                             arg, ['type'], [])
+                             arg, ['type'], ['if'])
             typ = arg['type']
         else:
             typ = arg
@@ -1438,8 +1438,8 @@ class QAPISchemaMember(object):
 
 
 class QAPISchemaObjectTypeMember(QAPISchemaMember):
-    def __init__(self, name, typ, optional):
-        QAPISchemaMember.__init__(self, name)
+    def __init__(self, name, typ, optional, ifcond=None):
+        QAPISchemaMember.__init__(self, name, ifcond)
         assert isinstance(typ, str)
         assert isinstance(optional, bool)
         self._type_name = typ
@@ -1762,7 +1762,7 @@ class QAPISchema(object):
             name, info, doc, ifcond,
             self._make_enum_members(data), prefix))
 
-    def _make_member(self, name, typ, info):
+    def _make_member(self, name, typ, ifcond, info):
         optional = False
         if name.startswith('*'):
             name = name[1:]
@@ -1770,10 +1770,10 @@ class QAPISchema(object):
         if isinstance(typ, list):
             assert len(typ) == 1
             typ = self._make_array_type(typ[0], info)
-        return QAPISchemaObjectTypeMember(name, typ, optional)
+        return QAPISchemaObjectTypeMember(name, typ, optional, ifcond)
 
     def _make_members(self, data, info):
-        return [self._make_member(key, value['type'], info)
+        return [self._make_member(key, value['type'], value.get('if'), info)
                 for (key, value) in data.items()]
 
     def _def_struct_type(self, expr, info, doc):
@@ -1794,7 +1794,7 @@ class QAPISchema(object):
             typ = self._make_array_type(typ[0], info)
         typ = self._make_implicit_object_type(
             typ, info, None, self.lookup_type(typ),
-            'wrapper', [self._make_member('data', typ, info)])
+            'wrapper', [self._make_member('data', typ, None, info)])
         return QAPISchemaObjectTypeVariant(case, typ)
 
     def _def_union_type(self, expr, info, doc):
diff --git a/docs/devel/qapi-code-gen.txt b/docs/devel/qapi-code-gen.txt
index 08c5ef97ff..e6fff32241 100644
--- a/docs/devel/qapi-code-gen.txt
+++ b/docs/devel/qapi-code-gen.txt
@@ -752,6 +752,16 @@ gets its generated code guarded like this:
  #endif /* defined(HAVE_BAR) */
  #endif /* defined(CONFIG_FOO) */
 
+Where a member can be defined with a single string value for its type,
+it is also possible to supply a dictionary instead with both 'type'
+and 'if' keys. (TODO: union and alternate)
+
+Example: a conditional 'bar' member
+
+{ 'struct': 'IfStruct', 'data':
+  { 'foo': 'int',
+    'bar': { 'type': 'int', 'if': 'defined(IFCOND)'} } }
+
 An enum value can be replaced by a dictionary with a 'name' and a 'if'
 key.
 
diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index 94570154c9..b8565a3913 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -201,7 +201,9 @@
 
 # test 'if' condition handling
 
-{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' },
+{ 'struct': 'TestIfStruct', 'data':
+  { 'foo': 'int',
+    'bar': { 'type': 'int', 'if': 'defined(TEST_IF_STRUCT_BAR)'} },
   'if': 'defined(TEST_IF_STRUCT)' }
 
 { 'enum': 'TestIfEnum', 'data':
@@ -214,11 +216,15 @@
 { 'alternate': 'TestIfAlternate', 'data': { 'foo': 'int', 'bar': 'TestStruct' },
   'if': 'defined(TEST_IF_ALT) && defined(TEST_IF_STRUCT)' }
 
-{ 'command': 'TestIfCmd', 'data': { 'foo': 'TestIfStruct', 'bar': 'TestIfEnum' },
+{ 'command': 'TestIfCmd', 'data':
+  { 'foo': 'TestIfStruct',
+    'bar': { 'type': 'TestIfEnum', 'if': 'defined(TEST_IF_CMD_BAR)' } },
   'returns': 'UserDefThree',
   'if': ['defined(TEST_IF_CMD)', 'defined(TEST_IF_STRUCT)'] }
 
 { 'command': 'TestCmdReturnDefThree', 'returns': 'UserDefThree' }
 
-{ 'event': 'TestIfEvent', 'data': { 'foo': 'TestIfStruct' },
+{ 'event': 'TestIfEvent', 'data':
+  { 'foo': 'TestIfStruct',
+    'bar': { 'type': 'TestIfEnum', 'if': 'defined(TEST_IF_EVT_BAR)' } },
   'if': 'defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)' }
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index 130199fe8f..23fb078b85 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -268,6 +268,8 @@ command __org.qemu_x-command q_obj___org.qemu_x-command-arg -> __org.qemu_x-Unio
    gen=True success_response=True boxed=False oob=False preconfig=False
 object TestIfStruct
     member foo: int optional=False
+    member bar: int optional=False
+        if ['defined(TEST_IF_STRUCT_BAR)']
     if ['defined(TEST_IF_STRUCT)']
 enum TestIfEnum
     member foo
@@ -292,6 +294,7 @@ alternate TestIfAlternate
 object q_obj_TestIfCmd-arg
     member foo: TestIfStruct optional=False
     member bar: TestIfEnum optional=False
+        if ['defined(TEST_IF_CMD_BAR)']
     if ['defined(TEST_IF_CMD)', 'defined(TEST_IF_STRUCT)']
 command TestIfCmd q_obj_TestIfCmd-arg -> UserDefThree
    gen=True success_response=True boxed=False oob=False preconfig=False
@@ -300,6 +303,8 @@ command TestCmdReturnDefThree None -> UserDefThree
    gen=True success_response=True boxed=False oob=False preconfig=False
 object q_obj_TestIfEvent-arg
     member foo: TestIfStruct optional=False
+    member bar: TestIfEnum optional=False
+        if ['defined(TEST_IF_EVT_BAR)']
     if ['defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)']
 event TestIfEvent q_obj_TestIfEvent-arg
    boxed=False
diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
index aadf252d9d..27081cb50c 100644
--- a/tests/qapi-schema/test-qapi.py
+++ b/tests/qapi-schema/test-qapi.py
@@ -39,6 +39,7 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
         for m in members:
             print('    member %s: %s optional=%s'
                   % (m.name, m.type.name, m.optional))
+            self._print_if(m.ifcond, 8)
         self._print_variants(variants)
         self._print_if(ifcond)
 
-- 
2.20.0.rc1

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

* [Qemu-devel] [PATCH for-4.0 v7 15/27] qapi: add an error in case a discriminator is conditional
  2018-12-08 11:15 [Qemu-devel] [PATCH for-4.0 v7 00/27] Hi, Marc-André Lureau
                   ` (13 preceding siblings ...)
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 14/27] qapi: add 'if' to implicit struct members Marc-André Lureau
@ 2018-12-08 11:15 ` Marc-André Lureau
  2018-12-10 17:31   ` Markus Armbruster
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 16/27] qapi: add 'if' to union members Marc-André Lureau
                   ` (13 subsequent siblings)
  28 siblings, 1 reply; 69+ messages in thread
From: Marc-André Lureau @ 2018-12-08 11:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Marc-André Lureau, Michael Roth

Making a discriminator conditional doesn't make much sense. Instead,
the union could be made conditional.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 scripts/qapi/common.py                          |  8 ++++++++
 tests/Makefile.include                          |  1 +
 .../flat-union-invalid-if-discriminator.err     |  1 +
 .../flat-union-invalid-if-discriminator.exit    |  1 +
 .../flat-union-invalid-if-discriminator.json    | 17 +++++++++++++++++
 .../flat-union-invalid-if-discriminator.out     |  0
 6 files changed, 28 insertions(+)
 create mode 100644 tests/qapi-schema/flat-union-invalid-if-discriminator.err
 create mode 100644 tests/qapi-schema/flat-union-invalid-if-discriminator.exit
 create mode 100644 tests/qapi-schema/flat-union-invalid-if-discriminator.json
 create mode 100644 tests/qapi-schema/flat-union-invalid-if-discriminator.out

diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index aec51acb07..f79b3fad54 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -578,6 +578,7 @@ def find_alternate_member_qtype(qapi_type):
 # Return the discriminator enum define if discriminator is specified as an
 # enum type, otherwise return None.
 def discriminator_find_enum_define(expr):
+    name = expr['union']
     base = expr.get('base')
     discriminator = expr.get('discriminator')
 
@@ -594,6 +595,7 @@ def discriminator_find_enum_define(expr):
 
     if isinstance(discriminator_value, dict):
         discriminator_value = discriminator_value['type']
+
     return enum_types.get(discriminator_value)
 
 
@@ -800,7 +802,12 @@ def check_union(expr, info):
                                "struct '%s'"
                                % (discriminator, base))
         if isinstance(discriminator_value, dict):
+            if discriminator_value.get('if'):
+                raise QAPISemError(info, 'The discriminator %s.%s for union %s '
+                                   'must not be conditional' %
+                                   (base, discriminator, name))
             discriminator_value = discriminator_value['type']
+
         enum_define = enum_types.get(discriminator_value)
         allow_metas = ['struct']
         # Do not allow string discriminator
@@ -1023,6 +1030,7 @@ def check_exprs(exprs):
 
         if 'include' in expr:
             continue
+        info = expr_elem['info']
         if 'union' in expr and not discriminator_find_enum_define(expr):
             name = '%sKind' % expr['union']
         elif 'alternate' in expr:
diff --git a/tests/Makefile.include b/tests/Makefile.include
index ea5d1e8787..3f5a1d0c30 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -409,6 +409,7 @@ qapi-schema += flat-union-inline-invalid-dict.json
 qapi-schema += flat-union-int-branch.json
 qapi-schema += flat-union-invalid-branch-key.json
 qapi-schema += flat-union-invalid-discriminator.json
+qapi-schema += flat-union-invalid-if-discriminator.json
 qapi-schema += flat-union-no-base.json
 qapi-schema += flat-union-optional-discriminator.json
 qapi-schema += flat-union-string-discriminator.json
diff --git a/tests/qapi-schema/flat-union-invalid-if-discriminator.err b/tests/qapi-schema/flat-union-invalid-if-discriminator.err
new file mode 100644
index 0000000000..0c94c9860d
--- /dev/null
+++ b/tests/qapi-schema/flat-union-invalid-if-discriminator.err
@@ -0,0 +1 @@
+tests/qapi-schema/flat-union-invalid-if-discriminator.json:13: The discriminator TestBase.enum1 for union TestUnion must not be conditional
diff --git a/tests/qapi-schema/flat-union-invalid-if-discriminator.exit b/tests/qapi-schema/flat-union-invalid-if-discriminator.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/flat-union-invalid-if-discriminator.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/flat-union-invalid-if-discriminator.json b/tests/qapi-schema/flat-union-invalid-if-discriminator.json
new file mode 100644
index 0000000000..618ec36396
--- /dev/null
+++ b/tests/qapi-schema/flat-union-invalid-if-discriminator.json
@@ -0,0 +1,17 @@
+{ 'enum': 'TestEnum',
+  'data': [ 'value1', 'value2' ] }
+
+{ 'struct': 'TestBase',
+  'data': { 'enum1': { 'type': 'TestEnum', 'if': 'FOO' } } }
+
+{ 'struct': 'TestTypeA',
+  'data': { 'string': 'str' } }
+
+{ 'struct': 'TestTypeB',
+  'data': { 'integer': 'int' } }
+
+{ 'union': 'TestUnion',
+  'base': 'TestBase',
+  'discriminator': 'enum1',
+  'data': { 'value1': 'TestTypeA',
+            'value2': 'TestTypeB' } }
diff --git a/tests/qapi-schema/flat-union-invalid-if-discriminator.out b/tests/qapi-schema/flat-union-invalid-if-discriminator.out
new file mode 100644
index 0000000000..e69de29bb2
-- 
2.20.0.rc1

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

* [Qemu-devel] [PATCH for-4.0 v7 16/27] qapi: add 'if' to union members
  2018-12-08 11:15 [Qemu-devel] [PATCH for-4.0 v7 00/27] Hi, Marc-André Lureau
                   ` (14 preceding siblings ...)
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 15/27] qapi: add an error in case a discriminator is conditional Marc-André Lureau
@ 2018-12-08 11:15 ` Marc-André Lureau
  2018-12-10 17:33   ` Markus Armbruster
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 17/27] qapi: simplify make_enum_members() Marc-André Lureau
                   ` (12 subsequent siblings)
  28 siblings, 1 reply; 69+ messages in thread
From: Marc-André Lureau @ 2018-12-08 11:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Marc-André Lureau, Michael Roth

Add 'if' key to union members:

{ 'union': 'TestIfUnion', 'data':
    'mem': { 'type': 'str', 'if': 'COND'} }

The generated code remains unconditional for now. Later patches
generate the conditionals.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 scripts/qapi/common.py                  | 17 +++++++++--------
 docs/devel/qapi-code-gen.txt            |  2 +-
 tests/qapi-schema/qapi-schema-test.json |  4 +++-
 tests/qapi-schema/qapi-schema-test.out  |  4 ++++
 tests/qapi-schema/test-qapi.py          |  1 +
 5 files changed, 18 insertions(+), 10 deletions(-)

diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index f79b3fad54..fd622313cb 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -823,7 +823,7 @@ def check_union(expr, info):
         check_name(info, "Member of union '%s'" % name, key)
         if isinstance(value, dict):
             check_known_keys(info, "member '%s' of union '%s'" % (key, name),
-                             value, ['type'], [])
+                             value, ['type'], ['if'])
             typ = value['type']
         else:
             typ = value
@@ -1513,8 +1513,8 @@ class QAPISchemaObjectTypeVariants(object):
 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
     role = 'branch'
 
-    def __init__(self, name, typ):
-        QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
+    def __init__(self, name, typ, ifcond=None):
+        QAPISchemaObjectTypeMember.__init__(self, name, typ, False, ifcond)
 
 
 class QAPISchemaAlternateType(QAPISchemaType):
@@ -1796,14 +1796,14 @@ class QAPISchema(object):
     def _make_variant(self, case, typ):
         return QAPISchemaObjectTypeVariant(case, typ)
 
-    def _make_simple_variant(self, case, typ, info):
+    def _make_simple_variant(self, case, typ, ifcond, info):
         if isinstance(typ, list):
             assert len(typ) == 1
             typ = self._make_array_type(typ[0], info)
         typ = self._make_implicit_object_type(
             typ, info, None, self.lookup_type(typ),
             'wrapper', [self._make_member('data', typ, None, info)])
-        return QAPISchemaObjectTypeVariant(case, typ)
+        return QAPISchemaObjectTypeVariant(case, typ, ifcond)
 
     def _def_union_type(self, expr, info, doc):
         name = expr['union']
@@ -1821,10 +1821,11 @@ class QAPISchema(object):
                         for (key, value) in data.items()]
             members = []
         else:
-            variants = [self._make_simple_variant(key, value['type'], info)
+            variants = [self._make_simple_variant(key, value['type'],
+                                                  value.get('if'), info)
                         for (key, value) in data.items()]
-            typ = self._make_implicit_enum_type(name, info, ifcond,
-                                                [v.name for v in variants])
+            enum = [{'name': v.name, 'if': v.ifcond} for v in variants]
+            typ = self._make_implicit_enum_type(name, info, ifcond, enum)
             tag_member = QAPISchemaObjectTypeMember('type', typ, False)
             members = [tag_member]
         self._def_entity(
diff --git a/docs/devel/qapi-code-gen.txt b/docs/devel/qapi-code-gen.txt
index e6fff32241..6f2457a2e0 100644
--- a/docs/devel/qapi-code-gen.txt
+++ b/docs/devel/qapi-code-gen.txt
@@ -754,7 +754,7 @@ gets its generated code guarded like this:
 
 Where a member can be defined with a single string value for its type,
 it is also possible to supply a dictionary instead with both 'type'
-and 'if' keys. (TODO: union and alternate)
+and 'if' keys. (TODO: alternate)
 
 Example: a conditional 'bar' member
 
diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index b8565a3913..6e9e4e14d9 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -210,7 +210,9 @@
   [ 'foo', { 'name' : 'bar', 'if': 'defined(TEST_IF_ENUM_BAR)' } ],
   'if': 'defined(TEST_IF_ENUM)' }
 
-{ 'union': 'TestIfUnion', 'data': { 'foo': 'TestStruct' },
+{ 'union': 'TestIfUnion', 'data':
+  { 'foo': 'TestStruct',
+    'union_bar': { 'type': 'str', 'if': 'defined(TEST_IF_UNION_BAR)'} },
   'if': 'defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)' }
 
 { 'alternate': 'TestIfAlternate', 'data': { 'foo': 'int', 'bar': 'TestStruct' },
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index 23fb078b85..a342ecfc55 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -280,11 +280,15 @@ object q_obj_TestStruct-wrapper
     member data: TestStruct optional=False
 enum TestIfUnionKind
     member foo
+    member union_bar
+        if ['defined(TEST_IF_UNION_BAR)']
     if ['defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)']
 object TestIfUnion
     member type: TestIfUnionKind optional=False
     tag type
     case foo: q_obj_TestStruct-wrapper
+    case union_bar: q_obj_str-wrapper
+        if ['defined(TEST_IF_UNION_BAR)']
     if ['defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)']
 alternate TestIfAlternate
     tag type
diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
index 27081cb50c..d592854601 100644
--- a/tests/qapi-schema/test-qapi.py
+++ b/tests/qapi-schema/test-qapi.py
@@ -68,6 +68,7 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
             print('    tag %s' % variants.tag_member.name)
             for v in variants.variants:
                 print('    case %s: %s' % (v.name, v.type.name))
+                QAPISchemaTestVisitor._print_if(v.ifcond, indent=8)
 
     @staticmethod
     def _print_if(ifcond, indent=4):
-- 
2.20.0.rc1

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

* [Qemu-devel] [PATCH for-4.0 v7 17/27] qapi: simplify make_enum_members()
  2018-12-08 11:15 [Qemu-devel] [PATCH for-4.0 v7 00/27] Hi, Marc-André Lureau
                   ` (15 preceding siblings ...)
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 16/27] qapi: add 'if' to union members Marc-André Lureau
@ 2018-12-08 11:15 ` Marc-André Lureau
  2018-12-11 12:05   ` Markus Armbruster
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 18/27] tests/qapi: add command with condition on union argument Marc-André Lureau
                   ` (11 subsequent siblings)
  28 siblings, 1 reply; 69+ messages in thread
From: Marc-André Lureau @ 2018-12-08 11:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Marc-André Lureau, Michael Roth

The function only receives the dictionary form of enum expressions
now, so we can make it shorter.

Suggested-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 scripts/qapi/common.py | 11 +----------
 1 file changed, 1 insertion(+), 10 deletions(-)

diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index fd622313cb..44020779dd 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -1714,16 +1714,7 @@ class QAPISchema(object):
                                             qtype_values, 'QTYPE'))
 
     def _make_enum_members(self, values):
-        enum = []
-        for v in values:
-            if isinstance(v, dict):
-                name = v['name']
-                ifcond = v.get('if')
-            else:
-                name = v
-                ifcond = None
-            enum.append(QAPISchemaMember(name, ifcond))
-        return enum
+        return [QAPISchemaMember(v['name'], v.get('if')) for v in values]
 
     def _make_implicit_enum_type(self, name, info, ifcond, values):
         # See also QAPISchemaObjectTypeMember._pretty_owner()
-- 
2.20.0.rc1

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

* [Qemu-devel] [PATCH for-4.0 v7 18/27] tests/qapi: add command with condition on union argument
  2018-12-08 11:15 [Qemu-devel] [PATCH for-4.0 v7 00/27] Hi, Marc-André Lureau
                   ` (16 preceding siblings ...)
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 17/27] qapi: simplify make_enum_members() Marc-André Lureau
@ 2018-12-08 11:15 ` Marc-André Lureau
  2018-12-11 12:09   ` Markus Armbruster
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 19/27] qapi: add 'if' to alternate members Marc-André Lureau
                   ` (10 subsequent siblings)
  28 siblings, 1 reply; 69+ messages in thread
From: Marc-André Lureau @ 2018-12-08 11:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Marc-André Lureau, Michael Roth

For completeness.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 tests/qapi-schema/qapi-schema-test.json | 3 +++
 tests/qapi-schema/qapi-schema-test.out  | 6 ++++++
 2 files changed, 9 insertions(+)

diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index 6e9e4e14d9..0d28475f4c 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -215,6 +215,9 @@
     'union_bar': { 'type': 'str', 'if': 'defined(TEST_IF_UNION_BAR)'} },
   'if': 'defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)' }
 
+{ 'command': 'TestIfUnionCmd', 'data': { 'union_cmd_arg': 'TestIfUnion' },
+  'if': 'defined(TEST_IF_UNION)' }
+
 { 'alternate': 'TestIfAlternate', 'data': { 'foo': 'int', 'bar': 'TestStruct' },
   'if': 'defined(TEST_IF_ALT) && defined(TEST_IF_STRUCT)' }
 
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index a342ecfc55..aedc668aa4 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -290,6 +290,12 @@ object TestIfUnion
     case union_bar: q_obj_str-wrapper
         if ['defined(TEST_IF_UNION_BAR)']
     if ['defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)']
+object q_obj_TestIfUnionCmd-arg
+    member union_cmd_arg: TestIfUnion optional=False
+    if ['defined(TEST_IF_UNION)']
+command TestIfUnionCmd q_obj_TestIfUnionCmd-arg -> None
+   gen=True success_response=True boxed=False oob=False preconfig=False
+    if ['defined(TEST_IF_UNION)']
 alternate TestIfAlternate
     tag type
     case foo: int
-- 
2.20.0.rc1

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

* [Qemu-devel] [PATCH for-4.0 v7 19/27] qapi: add 'if' to alternate members
  2018-12-08 11:15 [Qemu-devel] [PATCH for-4.0 v7 00/27] Hi, Marc-André Lureau
                   ` (17 preceding siblings ...)
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 18/27] tests/qapi: add command with condition on union argument Marc-André Lureau
@ 2018-12-08 11:15 ` Marc-André Lureau
  2018-12-11 12:21   ` Markus Armbruster
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 20/27] tests/qapi: add command with condition on alternate argument Marc-André Lureau
                   ` (9 subsequent siblings)
  28 siblings, 1 reply; 69+ messages in thread
From: Marc-André Lureau @ 2018-12-08 11:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Marc-André Lureau, Michael Roth

Add 'if' key to alternate members:

{ 'alternate': 'TestIfAlternate', 'data':
  { 'alt': { 'type': 'TestStruct', 'if': 'COND' } } }

Generated code is not changed by this patch but with "qapi: add #if
conditions to generated code".

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 scripts/qapi/common.py                  | 10 +++++-----
 docs/devel/qapi-code-gen.txt            |  2 +-
 tests/qapi-schema/qapi-schema-test.json |  4 +++-
 tests/qapi-schema/qapi-schema-test.out  |  1 +
 4 files changed, 10 insertions(+), 7 deletions(-)

diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index 44020779dd..a70d6dec3b 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -857,7 +857,7 @@ def check_alternate(expr, info):
         if isinstance(value, dict):
             check_known_keys(info,
                              "member '%s' of alternate '%s'" % (key, name),
-                             value, ['type'], [])
+                             value, ['type'], ['if'])
             typ = value['type']
         else:
             typ = value
@@ -1784,8 +1784,8 @@ class QAPISchema(object):
                                               self._make_members(data, info),
                                               None))
 
-    def _make_variant(self, case, typ):
-        return QAPISchemaObjectTypeVariant(case, typ)
+    def _make_variant(self, case, typ, ifcond):
+        return QAPISchemaObjectTypeVariant(case, typ, ifcond)
 
     def _make_simple_variant(self, case, typ, ifcond, info):
         if isinstance(typ, list):
@@ -1808,7 +1808,7 @@ class QAPISchema(object):
                 name, info, doc, ifcond,
                 'base', self._make_members(base, info))
         if tag_name:
-            variants = [self._make_variant(key, value['type'])
+            variants = [self._make_variant(key, value['type'], value.get('if'))
                         for (key, value) in data.items()]
             members = []
         else:
@@ -1829,7 +1829,7 @@ class QAPISchema(object):
         name = expr['alternate']
         data = expr['data']
         ifcond = expr.get('if')
-        variants = [self._make_variant(key, value['type'])
+        variants = [self._make_variant(key, value['type'], value.get('if'))
                     for (key, value) in data.items()]
         tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
         self._def_entity(
diff --git a/docs/devel/qapi-code-gen.txt b/docs/devel/qapi-code-gen.txt
index 6f2457a2e0..5ea62cb111 100644
--- a/docs/devel/qapi-code-gen.txt
+++ b/docs/devel/qapi-code-gen.txt
@@ -754,7 +754,7 @@ gets its generated code guarded like this:
 
 Where a member can be defined with a single string value for its type,
 it is also possible to supply a dictionary instead with both 'type'
-and 'if' keys. (TODO: alternate)
+and 'if' keys.
 
 Example: a conditional 'bar' member
 
diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index 0d28475f4c..d58cc49028 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -218,7 +218,9 @@
 { 'command': 'TestIfUnionCmd', 'data': { 'union_cmd_arg': 'TestIfUnion' },
   'if': 'defined(TEST_IF_UNION)' }
 
-{ 'alternate': 'TestIfAlternate', 'data': { 'foo': 'int', 'bar': 'TestStruct' },
+{ 'alternate': 'TestIfAlternate', 'data':
+  { 'foo': 'int',
+    'bar': { 'type': 'TestStruct', 'if': 'defined(TEST_IF_ALT_BAR)'} },
   'if': 'defined(TEST_IF_ALT) && defined(TEST_IF_STRUCT)' }
 
 { 'command': 'TestIfCmd', 'data':
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index aedc668aa4..59d7ed17a1 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -300,6 +300,7 @@ alternate TestIfAlternate
     tag type
     case foo: int
     case bar: TestStruct
+        if ['defined(TEST_IF_ALT_BAR)']
     if ['defined(TEST_IF_ALT) && defined(TEST_IF_STRUCT)']
 object q_obj_TestIfCmd-arg
     member foo: TestIfStruct optional=False
-- 
2.20.0.rc1

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

* [Qemu-devel] [PATCH for-4.0 v7 20/27] tests/qapi: add command with condition on alternate argument
  2018-12-08 11:15 [Qemu-devel] [PATCH for-4.0 v7 00/27] Hi, Marc-André Lureau
                   ` (18 preceding siblings ...)
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 19/27] qapi: add 'if' to alternate members Marc-André Lureau
@ 2018-12-08 11:15 ` Marc-André Lureau
  2018-12-11 12:20   ` Markus Armbruster
  2018-12-08 11:16 ` [Qemu-devel] [PATCH for-4.0 v7 21/27] qapi: add #if conditions to generated code members Marc-André Lureau
                   ` (8 subsequent siblings)
  28 siblings, 1 reply; 69+ messages in thread
From: Marc-André Lureau @ 2018-12-08 11:15 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Marc-André Lureau, Michael Roth

For completeness.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 tests/qapi-schema/qapi-schema-test.json | 3 +++
 tests/qapi-schema/qapi-schema-test.out  | 6 ++++++
 2 files changed, 9 insertions(+)

diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index d58cc49028..cb0857df52 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -223,6 +223,9 @@
     'bar': { 'type': 'TestStruct', 'if': 'defined(TEST_IF_ALT_BAR)'} },
   'if': 'defined(TEST_IF_ALT) && defined(TEST_IF_STRUCT)' }
 
+{ 'command': 'TestIfAlternateCmd', 'data': { 'alt_cmd_arg': 'TestIfAlternate' },
+  'if': 'defined(TEST_IF_ALT)' }
+
 { 'command': 'TestIfCmd', 'data':
   { 'foo': 'TestIfStruct',
     'bar': { 'type': 'TestIfEnum', 'if': 'defined(TEST_IF_CMD_BAR)' } },
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out
index 59d7ed17a1..9464101d26 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -302,6 +302,12 @@ alternate TestIfAlternate
     case bar: TestStruct
         if ['defined(TEST_IF_ALT_BAR)']
     if ['defined(TEST_IF_ALT) && defined(TEST_IF_STRUCT)']
+object q_obj_TestIfAlternateCmd-arg
+    member alt_cmd_arg: TestIfAlternate optional=False
+    if ['defined(TEST_IF_ALT)']
+command TestIfAlternateCmd q_obj_TestIfAlternateCmd-arg -> None
+   gen=True success_response=True boxed=False oob=False preconfig=False
+    if ['defined(TEST_IF_ALT)']
 object q_obj_TestIfCmd-arg
     member foo: TestIfStruct optional=False
     member bar: TestIfEnum optional=False
-- 
2.20.0.rc1

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

* [Qemu-devel] [PATCH for-4.0 v7 21/27] qapi: add #if conditions to generated code members
  2018-12-08 11:15 [Qemu-devel] [PATCH for-4.0 v7 00/27] Hi, Marc-André Lureau
                   ` (19 preceding siblings ...)
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 20/27] tests/qapi: add command with condition on alternate argument Marc-André Lureau
@ 2018-12-08 11:16 ` Marc-André Lureau
  2018-12-11 13:17   ` Markus Armbruster
  2018-12-08 11:16 ` [Qemu-devel] [PATCH for-4.0 v7 22/27] qapi: add 'If:' condition to enum values documentation Marc-André Lureau
                   ` (7 subsequent siblings)
  28 siblings, 1 reply; 69+ messages in thread
From: Marc-André Lureau @ 2018-12-08 11:16 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Marc-André Lureau, Michael Roth

Wrap generated enum/struct members and code with #if/#endif, using the
.ifcond members added in the previous patches.

Some types generate both enum and struct members for example, so a
step-by-step is unnecessarily complicated to deal with (it would
easily generate invalid intermediary code).

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 scripts/qapi/common.py     |  4 ++++
 scripts/qapi/introspect.py | 13 +++++++++----
 scripts/qapi/types.py      |  4 ++++
 scripts/qapi/visit.py      |  6 ++++++
 4 files changed, 23 insertions(+), 4 deletions(-)

diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index a70d6dec3b..3a29812ef2 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -2108,11 +2108,13 @@ const QEnumLookup %(c_name)s_lookup = {
 ''',
                 c_name=c_name(name))
     for m in members:
+        ret += gen_if(m.ifcond)
         index = c_enum_const(name, m.name, prefix)
         ret += mcgen('''
         [%(index)s] = "%(name)s",
 ''',
                      index=index, name=m.name)
+        ret += gen_endif(m.ifcond)
 
     ret += mcgen('''
     },
@@ -2134,10 +2136,12 @@ typedef enum %(c_name)s {
                 c_name=c_name(name))
 
     for m in enum_members:
+        ret += gen_if(m.ifcond)
         ret += mcgen('''
     %(c_enum)s,
 ''',
                      c_enum=c_enum_const(name, m.name, prefix))
+        ret += gen_endif(m.ifcond)
 
     ret += mcgen('''
 } %(c_name)s;
diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py
index 417625d54b..77087f629b 100644
--- a/scripts/qapi/introspect.py
+++ b/scripts/qapi/introspect.py
@@ -162,6 +162,8 @@ const QLitObject %(c_name)s = %(c_string)s;
         ret = {'name': member.name, 'type': self._use_type(member.type)}
         if member.optional:
             ret['default'] = None
+        if member.ifcond:
+            ret = (ret, {'if': member.ifcond})
         return ret
 
     def _gen_variants(self, tag_name, variants):
@@ -169,14 +171,16 @@ const QLitObject %(c_name)s = %(c_string)s;
                 'variants': [self._gen_variant(v) for v in variants]}
 
     def _gen_variant(self, variant):
-        return {'case': variant.name, 'type': self._use_type(variant.type)}
+        return ({'case': variant.name, 'type': self._use_type(variant.type)},
+                {'if': variant.ifcond})
 
     def visit_builtin_type(self, name, info, json_type):
         self._gen_qlit(name, 'builtin', {'json-type': json_type}, [])
 
     def visit_enum_type(self, name, info, ifcond, members, prefix):
         self._gen_qlit(name, 'enum',
-                       {'values': [m.name for m in members]}, ifcond)
+                       {'values': [(m.name, {'if': m.ifcond}) for m in members]},
+                       ifcond)
 
     def visit_array_type(self, name, info, ifcond, element_type):
         element = self._use_type(element_type)
@@ -192,8 +196,9 @@ const QLitObject %(c_name)s = %(c_string)s;
 
     def visit_alternate_type(self, name, info, ifcond, variants):
         self._gen_qlit(name, 'alternate',
-                       {'members': [{'type': self._use_type(m.type)}
-                                    for m in variants.variants]}, ifcond)
+                       {'members': [
+                           ({'type': self._use_type(m.type)}, {'if': m.ifcond})
+                           for m in variants.variants]}, ifcond)
 
     def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
                       success_response, boxed, allow_oob, allow_preconfig):
diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py
index 0404710bbd..e2ee9f3b72 100644
--- a/scripts/qapi/types.py
+++ b/scripts/qapi/types.py
@@ -43,6 +43,7 @@ struct %(c_name)s {
 def gen_struct_members(members):
     ret = ''
     for memb in members:
+        ret += gen_if(memb.ifcond)
         if memb.optional:
             ret += mcgen('''
     bool has_%(c_name)s;
@@ -52,6 +53,7 @@ def gen_struct_members(members):
     %(c_type)s %(c_name)s;
 ''',
                      c_type=memb.type.c_type(), c_name=c_name(memb.name))
+        ret += gen_endif(memb.ifcond)
     return ret
 
 
@@ -131,11 +133,13 @@ def gen_variants(variants):
     for var in variants.variants:
         if var.type.name == 'q_empty':
             continue
+        ret += gen_if(var.ifcond)
         ret += mcgen('''
         %(c_type)s %(c_name)s;
 ''',
                      c_type=var.type.c_unboxed_type(),
                      c_name=c_name(var.name))
+        ret += gen_endif(var.ifcond)
 
     ret += mcgen('''
     } u;
diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py
index 24f85a2e85..82eab72b21 100644
--- a/scripts/qapi/visit.py
+++ b/scripts/qapi/visit.py
@@ -54,6 +54,7 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
                      c_type=base.c_name())
 
     for memb in members:
+        ret += gen_if(memb.ifcond)
         if memb.optional:
             ret += mcgen('''
     if (visit_optional(v, "%(name)s", &obj->has_%(c_name)s)) {
@@ -73,6 +74,7 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
             ret += mcgen('''
     }
 ''')
+        ret += gen_endif(memb.ifcond)
 
     if variants:
         ret += mcgen('''
@@ -84,6 +86,7 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
             case_str = c_enum_const(variants.tag_member.type.name,
                                     var.name,
                                     variants.tag_member.type.prefix)
+            ret += gen_if(var.ifcond)
             if var.type.name == 'q_empty':
                 # valid variant and nothing to do
                 ret += mcgen('''
@@ -100,6 +103,7 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
                              case=case_str,
                              c_type=var.type.c_name(), c_name=c_name(var.name))
 
+            ret += gen_endif(var.ifcond)
         ret += mcgen('''
     default:
         abort();
@@ -190,6 +194,7 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
                 c_name=c_name(name))
 
     for var in variants.variants:
+        ret += gen_if(var.ifcond)
         ret += mcgen('''
     case %(case)s:
 ''',
@@ -217,6 +222,7 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
         ret += mcgen('''
         break;
 ''')
+        ret += gen_endif(var.ifcond)
 
     ret += mcgen('''
     case QTYPE_NONE:
-- 
2.20.0.rc1

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

* [Qemu-devel] [PATCH for-4.0 v7 22/27] qapi: add 'If:' condition to enum values documentation
  2018-12-08 11:15 [Qemu-devel] [PATCH for-4.0 v7 00/27] Hi, Marc-André Lureau
                   ` (20 preceding siblings ...)
  2018-12-08 11:16 ` [Qemu-devel] [PATCH for-4.0 v7 21/27] qapi: add #if conditions to generated code members Marc-André Lureau
@ 2018-12-08 11:16 ` Marc-André Lureau
  2018-12-11 16:00   ` Markus Armbruster
  2018-12-08 11:16 ` [Qemu-devel] [PATCH for-4.0 v7 23/27] qapi: add 'If:' condition to struct members documentation Marc-André Lureau
                   ` (6 subsequent siblings)
  28 siblings, 1 reply; 69+ messages in thread
From: Marc-André Lureau @ 2018-12-08 11:16 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Marc-André Lureau, Michael Roth

Use a common function to generate the "If:..." line.

While at it, get rid of the existing \n\n (no idea why it was
there). Use a line-break in member description, this seems to look
slightly better in the plaintext version.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 scripts/qapi/doc.py             | 24 +++++++++++++++---------
 tests/qapi-schema/doc-good.json |  4 +++-
 tests/qapi-schema/doc-good.out  |  1 +
 tests/qapi-schema/doc-good.texi |  2 +-
 4 files changed, 20 insertions(+), 11 deletions(-)

diff --git a/scripts/qapi/doc.py b/scripts/qapi/doc.py
index 76cb186ff9..2133ded47e 100755
--- a/scripts/qapi/doc.py
+++ b/scripts/qapi/doc.py
@@ -126,19 +126,26 @@ def texi_body(doc):
     return texi_format(doc.body.text)
 
 
-def texi_enum_value(value):
+def texi_if(ifcond, prefix='\n', suffix='\n'):
+    """Format the #if condition"""
+    return '%s@b{If:} @code{%s}%s' % (
+        prefix, ', '.join(ifcond), suffix) if ifcond else ''
+
+
+def texi_enum_value(value, desc, suffix=''):
     """Format a table of members item for an enumeration value"""
-    return '@item @code{%s}\n' % value.name
+    return '@item @code{%s}\n%s%s' % (
+        value.name, desc, texi_if(value.ifcond, prefix='@*'))
 
 
-def texi_member(member, suffix=''):
+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' % (
+    return '@item @code{%s%s}%s%s\n%s' % (
         member.name, membertype,
         ' (optional)' if member.optional else '',
-        suffix)
+        suffix, desc)
 
 
 def texi_members(doc, what, base, variants, member_func):
@@ -155,7 +162,7 @@ def texi_members(doc, what, base, variants, member_func):
             desc = 'One of ' + members_text + '\n'
         else:
             desc = 'Not documented\n'
-        items += member_func(section.member) + desc
+        items += member_func(section.member, desc)
     if base:
         items += '@item The members of @code{%s}\n' % base.doc_type()
     if variants:
@@ -165,7 +172,7 @@ def texi_members(doc, what, base, variants, member_func):
             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, when)
+                    items += member_func(m, suffix=when)
             else:
                 items += '@item The members of @code{%s}%s\n' % (
                     v.type.doc_type(), when)
@@ -185,8 +192,7 @@ def texi_sections(doc, ifcond):
             body += texi_example(section.text)
         else:
             body += texi_format(section.text)
-    if ifcond:
-        body += '\n\n@b{If:} @code{%s}' % ", ".join(ifcond)
+    body += texi_if(ifcond, suffix='')
     return body
 
 
diff --git a/tests/qapi-schema/doc-good.json b/tests/qapi-schema/doc-good.json
index 984cd8ed06..c7fe08c530 100644
--- a/tests/qapi-schema/doc-good.json
+++ b/tests/qapi-schema/doc-good.json
@@ -55,7 +55,9 @@
 #
 # @two is undocumented
 ##
-{ 'enum': 'Enum', 'data': [ 'one', 'two' ], 'if': 'defined(IFCOND)' }
+{ 'enum': 'Enum', 'data':
+  [ { 'name': 'one', 'if': 'defined(IFENUM)' }, 'two' ],
+  'if': 'defined(IFCOND)' }
 
 ##
 # @Base:
diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out
index c2fc5c774a..a05535b69b 100644
--- a/tests/qapi-schema/doc-good.out
+++ b/tests/qapi-schema/doc-good.out
@@ -11,6 +11,7 @@ enum QType
 module doc-good.json
 enum Enum
     member one
+        if ['defined(IFENUM)']
     member two
     if ['defined(IFCOND)']
 object Base
diff --git a/tests/qapi-schema/doc-good.texi b/tests/qapi-schema/doc-good.texi
index e42eace474..5c0231e0e6 100644
--- a/tests/qapi-schema/doc-good.texi
+++ b/tests/qapi-schema/doc-good.texi
@@ -84,12 +84,12 @@ Examples:
 @table @asis
 @item @code{one}
 The @emph{one} @{and only@}
+@*@b{If:} @code{defined(IFENUM)}
 @item @code{two}
 Not documented
 @end table
 @code{two} is undocumented
 
-
 @b{If:} @code{defined(IFCOND)}
 @end deftp
 
-- 
2.20.0.rc1

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

* [Qemu-devel] [PATCH for-4.0 v7 23/27] qapi: add 'If:' condition to struct members documentation
  2018-12-08 11:15 [Qemu-devel] [PATCH for-4.0 v7 00/27] Hi, Marc-André Lureau
                   ` (21 preceding siblings ...)
  2018-12-08 11:16 ` [Qemu-devel] [PATCH for-4.0 v7 22/27] qapi: add 'If:' condition to enum values documentation Marc-André Lureau
@ 2018-12-08 11:16 ` Marc-André Lureau
  2018-12-08 11:16 ` [Qemu-devel] [PATCH for-4.0 v7 24/27] qapi: add condition to variants documentation Marc-André Lureau
                   ` (5 subsequent siblings)
  28 siblings, 0 replies; 69+ messages in thread
From: Marc-André Lureau @ 2018-12-08 11:16 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Marc-André Lureau, Michael Roth

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 scripts/qapi/doc.py             | 4 ++--
 tests/qapi-schema/doc-good.json | 3 ++-
 tests/qapi-schema/doc-good.out  | 1 +
 tests/qapi-schema/doc-good.texi | 1 +
 4 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/scripts/qapi/doc.py b/scripts/qapi/doc.py
index 2133ded47e..e0cf94421c 100755
--- a/scripts/qapi/doc.py
+++ b/scripts/qapi/doc.py
@@ -142,10 +142,10 @@ 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' % (
+    return '@item @code{%s%s}%s%s\n%s%s' % (
         member.name, membertype,
         ' (optional)' if member.optional else '',
-        suffix, desc)
+        suffix, desc, texi_if(member.ifcond, prefix='@*'))
 
 
 def texi_members(doc, what, base, variants, member_func):
diff --git a/tests/qapi-schema/doc-good.json b/tests/qapi-schema/doc-good.json
index c7fe08c530..158443b1a3 100644
--- a/tests/qapi-schema/doc-good.json
+++ b/tests/qapi-schema/doc-good.json
@@ -72,7 +72,8 @@
 #
 # Another paragraph (but no @var: line)
 ##
-{ 'struct': 'Variant1', 'data': { 'var1': 'str' } }
+{ 'struct': 'Variant1',
+  'data': { 'var1': { 'type': 'str', 'if': 'defined(IFSTR)' } } }
 
 ##
 # @Variant2:
diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out
index a05535b69b..c310b47be2 100644
--- a/tests/qapi-schema/doc-good.out
+++ b/tests/qapi-schema/doc-good.out
@@ -18,6 +18,7 @@ object Base
     member base1: Enum optional=False
 object Variant1
     member var1: str optional=False
+        if ['defined(IFSTR)']
 object Variant2
 object Object
     base Base
diff --git a/tests/qapi-schema/doc-good.texi b/tests/qapi-schema/doc-good.texi
index 5c0231e0e6..0f8d69a378 100644
--- a/tests/qapi-schema/doc-good.texi
+++ b/tests/qapi-schema/doc-good.texi
@@ -119,6 +119,7 @@ Another paragraph (but no @code{var}: line)
 @table @asis
 @item @code{var1: string}
 Not documented
+@*@b{If:} @code{defined(IFSTR)}
 @end table
 
 @end deftp
-- 
2.20.0.rc1

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

* [Qemu-devel] [PATCH for-4.0 v7 24/27] qapi: add condition to variants documentation
  2018-12-08 11:15 [Qemu-devel] [PATCH for-4.0 v7 00/27] Hi, Marc-André Lureau
                   ` (22 preceding siblings ...)
  2018-12-08 11:16 ` [Qemu-devel] [PATCH for-4.0 v7 23/27] qapi: add 'If:' condition to struct members documentation Marc-André Lureau
@ 2018-12-08 11:16 ` Marc-André Lureau
  2018-12-08 11:16 ` [Qemu-devel] [PATCH for-4.0 v7 25/27] qapi: break long lines at 'data' member Marc-André Lureau
                   ` (4 subsequent siblings)
  28 siblings, 0 replies; 69+ messages in thread
From: Marc-André Lureau @ 2018-12-08 11:16 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Marc-André Lureau, Michael Roth

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 scripts/qapi/doc.py             | 4 ++--
 tests/qapi-schema/doc-good.json | 4 ++--
 tests/qapi-schema/doc-good.out  | 3 +++
 tests/qapi-schema/doc-good.texi | 4 ++--
 4 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/scripts/qapi/doc.py b/scripts/qapi/doc.py
index e0cf94421c..4008054eea 100755
--- a/scripts/qapi/doc.py
+++ b/scripts/qapi/doc.py
@@ -167,8 +167,8 @@ def texi_members(doc, what, base, variants, member_func):
         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"}' % (
-                variants.tag_member.name, v.name)
+            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:
diff --git a/tests/qapi-schema/doc-good.json b/tests/qapi-schema/doc-good.json
index 158443b1a3..afe46d93f0 100644
--- a/tests/qapi-schema/doc-good.json
+++ b/tests/qapi-schema/doc-good.json
@@ -86,13 +86,13 @@
 { 'union': 'Object',
   'base': 'Base',
   'discriminator': 'base1',
-  'data': { 'one': 'Variant1', 'two': 'Variant2' } }
+  'data': { 'one': 'Variant1', 'two': { 'type': 'Variant2', 'if': 'IFTWO' } } }
 
 ##
 # @SugaredUnion:
 ##
 { 'union': 'SugaredUnion',
-  'data': { 'one': 'Variant1', 'two': 'Variant2' } }
+  'data': { 'one': 'Variant1', 'two': { 'type': 'Variant2', 'if': 'IFTWO' } } }
 
 ##
 # == Another subsection
diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out
index c310b47be2..ed647b82be 100644
--- a/tests/qapi-schema/doc-good.out
+++ b/tests/qapi-schema/doc-good.out
@@ -25,6 +25,7 @@ object Object
     tag base1
     case one: Variant1
     case two: Variant2
+        if ['IFTWO']
 object q_obj_Variant1-wrapper
     member data: Variant1 optional=False
 object q_obj_Variant2-wrapper
@@ -32,11 +33,13 @@ object q_obj_Variant2-wrapper
 enum SugaredUnionKind
     member one
     member two
+        if ['IFTWO']
 object SugaredUnion
     member type: SugaredUnionKind optional=False
     tag type
     case one: q_obj_Variant1-wrapper
     case two: q_obj_Variant2-wrapper
+        if ['IFTWO']
 object q_obj_cmd-arg
     member arg1: int optional=False
     member arg2: str optional=True
diff --git a/tests/qapi-schema/doc-good.texi b/tests/qapi-schema/doc-good.texi
index 0f8d69a378..df08a89521 100644
--- a/tests/qapi-schema/doc-good.texi
+++ b/tests/qapi-schema/doc-good.texi
@@ -142,7 +142,7 @@ Not documented
 @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"}
+@item The members of @code{Variant2} when @code{base1} is @t{"two"} (@b{If:} @code{IFTWO})
 @end table
 
 @end deftp
@@ -158,7 +158,7 @@ Not documented
 @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"}
+@item @code{data: Variant2} when @code{type} is @t{"two"} (@b{If:} @code{IFTWO})
 @end table
 
 @end deftp
-- 
2.20.0.rc1

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

* [Qemu-devel] [PATCH for-4.0 v7 25/27] qapi: break long lines at 'data' member
  2018-12-08 11:15 [Qemu-devel] [PATCH for-4.0 v7 00/27] Hi, Marc-André Lureau
                   ` (23 preceding siblings ...)
  2018-12-08 11:16 ` [Qemu-devel] [PATCH for-4.0 v7 24/27] qapi: add condition to variants documentation Marc-André Lureau
@ 2018-12-08 11:16 ` Marc-André Lureau
  2018-12-11 16:07   ` Markus Armbruster
  2018-12-08 11:16 ` [Qemu-devel] [PATCH for-4.0 v7 26/27] qapi: add more conditions to SPICE Marc-André Lureau
                   ` (3 subsequent siblings)
  28 siblings, 1 reply; 69+ messages in thread
From: Marc-André Lureau @ 2018-12-08 11:16 UTC (permalink / raw)
  To: qemu-devel
  Cc: armbru, Marc-André Lureau, Eric Blake, Paolo Bonzini,
	Juan Quintela, Dr. David Alan Gilbert, Jason Wang, Stefan Berger,
	Gerd Hoffmann

Let's break the line before 'data'. While at it, improve a bit
indentation/spacing. (I removed some alignment which are not helping
much readability and become quickly inconsistent)

Suggested-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 qapi/block-core.json |  13 ++--
 qapi/char.json       | 138 +++++++++++++++++++++++++------------------
 qapi/migration.json  |   3 +-
 qapi/misc.json       |   7 ++-
 qapi/net.json        |   3 +-
 qapi/tpm.json        |   5 +-
 qapi/ui.json         |   3 +-
 7 files changed, 101 insertions(+), 71 deletions(-)

diff --git a/qapi/block-core.json b/qapi/block-core.json
index d4fe710836..92e0205d91 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -1143,8 +1143,10 @@
 # This command is now obsolete and will always return an error since 2.10
 #
 ##
-{ 'command': 'block_passwd', 'data': {'*device': 'str',
-                                      '*node-name': 'str', 'password': 'str'} }
+{ 'command': 'block_passwd',
+  'data': { '*device': 'str',
+            '*node-name': 'str',
+            'password': 'str' } }
 
 ##
 # @block_resize:
@@ -1171,9 +1173,10 @@
 # <- { "return": {} }
 #
 ##
-{ 'command': 'block_resize', 'data': { '*device': 'str',
-                                       '*node-name': 'str',
-                                       'size': 'int' }}
+{ 'command': 'block_resize',
+  'data': { '*device': 'str',
+            '*node-name': 'str',
+            'size': 'int' } }
 
 ##
 # @NewImageMode:
diff --git a/qapi/char.json b/qapi/char.json
index 79bac598a0..24628331ec 100644
--- a/qapi/char.json
+++ b/qapi/char.json
@@ -25,9 +25,10 @@
 #
 # Since: 0.14.0
 ##
-{ 'struct': 'ChardevInfo', 'data': {'label': 'str',
-                                  'filename': 'str',
-                                  'frontend-open': 'bool'} }
+{ 'struct': 'ChardevInfo',
+  'data': { 'label': 'str',
+            'filename': 'str',
+            'frontend-open': 'bool' } }
 
 ##
 # @query-chardev:
@@ -152,7 +153,8 @@
 #
 ##
 { 'command': 'ringbuf-write',
-  'data': {'device': 'str', 'data': 'str',
+  'data': { 'device': 'str',
+            'data': 'str',
            '*format': 'DataFormat'} }
 
 ##
@@ -202,8 +204,9 @@
 #
 # Since: 2.6
 ##
-{ 'struct': 'ChardevCommon', 'data': { '*logfile': 'str',
-                                       '*logappend': 'bool' } }
+{ 'struct': 'ChardevCommon',
+  'data': { '*logfile': 'str',
+            '*logappend': 'bool' } }
 
 ##
 # @ChardevFile:
@@ -217,9 +220,10 @@
 #
 # Since: 1.4
 ##
-{ 'struct': 'ChardevFile', 'data': { '*in' : 'str',
-                                   'out' : 'str',
-                                   '*append': 'bool' },
+{ 'struct': 'ChardevFile',
+  'data': { '*in': 'str',
+            'out': 'str',
+            '*append': 'bool' },
   'base': 'ChardevCommon' }
 
 ##
@@ -232,7 +236,8 @@
 #
 # Since: 1.4
 ##
-{ 'struct': 'ChardevHostdev', 'data': { 'device' : 'str' },
+{ 'struct': 'ChardevHostdev',
+  'data': { 'device': 'str' },
   'base': 'ChardevCommon' }
 
 ##
@@ -260,15 +265,16 @@
 #
 # Since: 1.4
 ##
-{ 'struct': 'ChardevSocket', 'data': { 'addr'       : 'SocketAddressLegacy',
-                                     '*tls-creds'  : 'str',
-                                     '*server'    : 'bool',
-                                     '*wait'      : 'bool',
-                                     '*nodelay'   : 'bool',
-                                     '*telnet'    : 'bool',
-                                     '*tn3270'    : 'bool',
-                                     '*websocket' : 'bool',
-                                     '*reconnect' : 'int' },
+{ 'struct': 'ChardevSocket',
+  'data': { 'addr': 'SocketAddressLegacy',
+            '*tls-creds': 'str',
+            '*server': 'bool',
+            '*wait': 'bool',
+            '*nodelay': 'bool',
+            '*telnet': 'bool',
+            '*tn3270': 'bool',
+            '*websocket': 'bool',
+            '*reconnect': 'int' },
   'base': 'ChardevCommon' }
 
 ##
@@ -281,8 +287,9 @@
 #
 # Since: 1.5
 ##
-{ 'struct': 'ChardevUdp', 'data': { 'remote' : 'SocketAddressLegacy',
-                                  '*local' : 'SocketAddressLegacy' },
+{ 'struct': 'ChardevUdp',
+  'data': { 'remote': 'SocketAddressLegacy',
+            '*local': 'SocketAddressLegacy' },
   'base': 'ChardevCommon' }
 
 ##
@@ -294,7 +301,8 @@
 #
 # Since: 1.5
 ##
-{ 'struct': 'ChardevMux', 'data': { 'chardev' : 'str' },
+{ 'struct': 'ChardevMux',
+  'data': { 'chardev': 'str' },
   'base': 'ChardevCommon' }
 
 ##
@@ -308,7 +316,8 @@
 #
 # Since: 1.5
 ##
-{ 'struct': 'ChardevStdio', 'data': { '*signal' : 'bool' },
+{ 'struct': 'ChardevStdio',
+  'data': { '*signal': 'bool' },
   'base': 'ChardevCommon' }
 
 
@@ -321,7 +330,8 @@
 #
 # Since: 1.5
 ##
-{ 'struct': 'ChardevSpiceChannel', 'data': { 'type'  : 'str' },
+{ 'struct': 'ChardevSpiceChannel',
+  'data': { 'type': 'str' },
   'base': 'ChardevCommon' }
 # TODO: 'if': 'defined(CONFIG_SPICE)'
 
@@ -334,7 +344,8 @@
 #
 # Since: 1.5
 ##
-{ 'struct': 'ChardevSpicePort', 'data': { 'fqdn'  : 'str' },
+{ 'struct': 'ChardevSpicePort',
+  'data': { 'fqdn': 'str' },
   'base': 'ChardevCommon' }
 # TODO: 'if': 'defined(CONFIG_SPICE)'
 
@@ -350,10 +361,11 @@
 #
 # Since: 1.5
 ##
-{ 'struct': 'ChardevVC', 'data': { '*width'  : 'int',
-                                 '*height' : 'int',
-                                 '*cols'   : 'int',
-                                 '*rows'   : 'int' },
+{ 'struct': 'ChardevVC',
+  'data': { '*width': 'int',
+            '*height': 'int',
+            '*cols': 'int',
+            '*rows': 'int' },
   'base': 'ChardevCommon' }
 
 ##
@@ -365,7 +377,8 @@
 #
 # Since: 1.5
 ##
-{ 'struct': 'ChardevRingbuf', 'data': { '*size'  : 'int' },
+{ 'struct': 'ChardevRingbuf',
+  'data': { '*size': 'int' },
   'base': 'ChardevCommon' }
 
 ##
@@ -375,29 +388,30 @@
 #
 # Since: 1.4 (testdev since 2.2, wctablet since 2.9)
 ##
-{ 'union': 'ChardevBackend', 'data': { 'file'   : 'ChardevFile',
-                                       'serial' : 'ChardevHostdev',
-                                       'parallel': 'ChardevHostdev',
-                                       'pipe'   : 'ChardevHostdev',
-                                       'socket' : 'ChardevSocket',
-                                       'udp'    : 'ChardevUdp',
-                                       'pty'    : 'ChardevCommon',
-                                       'null'   : 'ChardevCommon',
-                                       'mux'    : 'ChardevMux',
-                                       'msmouse': 'ChardevCommon',
-                                       'wctablet' : 'ChardevCommon',
-                                       'braille': 'ChardevCommon',
-                                       'testdev': 'ChardevCommon',
-                                       'stdio'  : 'ChardevStdio',
-                                       'console': 'ChardevCommon',
-                                       'spicevmc': 'ChardevSpiceChannel',
+{ 'union': 'ChardevBackend',
+  'data': { 'file': 'ChardevFile',
+            'serial': 'ChardevHostdev',
+            'parallel': 'ChardevHostdev',
+            'pipe': 'ChardevHostdev',
+            'socket': 'ChardevSocket',
+            'udp': 'ChardevUdp',
+            'pty': 'ChardevCommon',
+            'null': 'ChardevCommon',
+            'mux': 'ChardevMux',
+            'msmouse': 'ChardevCommon',
+            'wctablet': 'ChardevCommon',
+            'braille': 'ChardevCommon',
+            'testdev': 'ChardevCommon',
+            'stdio': 'ChardevStdio',
+            'console': 'ChardevCommon',
+            'spicevmc': 'ChardevSpiceChannel',
 # TODO: { 'type': 'ChardevSpiceChannel', 'if': 'defined(CONFIG_SPICE)' },
-                                       'spiceport': 'ChardevSpicePort',
+            'spiceport': 'ChardevSpicePort',
 # TODO: { 'type': 'ChardevSpicePort', 'if': 'defined(CONFIG_SPICE)' },
-                                       'vc'     : 'ChardevVC',
-                                       'ringbuf': 'ChardevRingbuf',
-                                       # next one is just for compatibility
-                                       'memory' : 'ChardevRingbuf' } }
+            'vc': 'ChardevVC',
+            'ringbuf': 'ChardevRingbuf',
+            # next one is just for compatibility
+            'memory': 'ChardevRingbuf' } }
 
 ##
 # @ChardevReturn:
@@ -409,7 +423,8 @@
 #
 # Since: 1.4
 ##
-{ 'struct' : 'ChardevReturn', 'data': { '*pty' : 'str' } }
+{ 'struct' : 'ChardevReturn',
+  'data': { '*pty': 'str' } }
 
 ##
 # @chardev-add:
@@ -442,8 +457,9 @@
 # <- { "return": { "pty" : "/dev/pty/42" } }
 #
 ##
-{ 'command': 'chardev-add', 'data': {'id'      : 'str',
-                                     'backend' : 'ChardevBackend' },
+{ 'command': 'chardev-add',
+  'data': { 'id': 'str',
+            'backend': 'ChardevBackend' },
   'returns': 'ChardevReturn' }
 
 ##
@@ -482,8 +498,9 @@
 # <- {"return": {}}
 #
 ##
-{ 'command': 'chardev-change', 'data': {'id'      : 'str',
-                                        'backend' : 'ChardevBackend' },
+{ 'command': 'chardev-change',
+  'data': { 'id': 'str',
+            'backend': 'ChardevBackend' },
   'returns': 'ChardevReturn' }
 
 ##
@@ -503,7 +520,8 @@
 # <- { "return": {} }
 #
 ##
-{ 'command': 'chardev-remove', 'data': {'id': 'str'} }
+{ 'command': 'chardev-remove',
+  'data': { 'id': 'str' } }
 
 ##
 # @chardev-send-break:
@@ -522,7 +540,8 @@
 # <- { "return": {} }
 #
 ##
-{ 'command': 'chardev-send-break', 'data': {'id': 'str'} }
+{ 'command': 'chardev-send-break',
+  'data': { 'id': 'str' } }
 
 ##
 # @VSERPORT_CHANGE:
@@ -543,4 +562,5 @@
 #
 ##
 { 'event': 'VSERPORT_CHANGE',
-  'data': { 'id': 'str', 'open': 'bool' } }
+  'data': { 'id': 'str',
+            'open': 'bool' } }
diff --git a/qapi/migration.json b/qapi/migration.json
index 38d4c41d88..5fd33316a0 100644
--- a/qapi/migration.json
+++ b/qapi/migration.json
@@ -1356,7 +1356,8 @@
 #
 # Since: 3.0
 ##
-{ 'command': 'migrate-recover', 'data': { 'uri': 'str' },
+{ 'command': 'migrate-recover',
+  'data': { 'uri': 'str' },
   'allow-oob': true }
 
 ##
diff --git a/qapi/misc.json b/qapi/misc.json
index 6c1c5c0a37..012048a071 100644
--- a/qapi/misc.json
+++ b/qapi/misc.json
@@ -2381,7 +2381,9 @@
 # <- { "return": { "fdset-id": 1, "fd": 3 } }
 #
 ##
-{ 'command': 'add-fd', 'data': {'*fdset-id': 'int', '*opaque': 'str'},
+{ 'command': 'add-fd',
+  'data': { '*fdset-id': 'int',
+            '*opaque': 'str' },
   'returns': 'AddfdInfo' }
 
 ##
@@ -2653,7 +2655,8 @@
 #    }
 #
 ##
-{'command': 'query-command-line-options', 'data': { '*option': 'str' },
+{'command': 'query-command-line-options',
+ 'data': { '*option': 'str' },
  'returns': ['CommandLineOptionInfo'],
  'allow-preconfig': true }
 
diff --git a/qapi/net.json b/qapi/net.json
index 8f99fd911d..a1a0f39f74 100644
--- a/qapi/net.json
+++ b/qapi/net.json
@@ -657,7 +657,8 @@
 #    }
 #
 ##
-{ 'command': 'query-rx-filter', 'data': { '*name': 'str' },
+{ 'command': 'query-rx-filter',
+  'data': { '*name': 'str' },
   'returns': ['RxFilterInfo'] }
 
 ##
diff --git a/qapi/tpm.json b/qapi/tpm.json
index d50deef5e9..b30323bb6b 100644
--- a/qapi/tpm.json
+++ b/qapi/tpm.json
@@ -76,8 +76,9 @@
 #
 # Since: 1.5
 ##
-{ 'struct': 'TPMPassthroughOptions', 'data': { '*path' : 'str',
-                                             '*cancel-path' : 'str'} }
+{ 'struct': 'TPMPassthroughOptions',
+  'data': { '*path': 'str',
+            '*cancel-path': 'str' } }
 
 ##
 # @TPMEmulatorOptions:
diff --git a/qapi/ui.json b/qapi/ui.json
index fd39acb5c3..5ad13248d5 100644
--- a/qapi/ui.json
+++ b/qapi/ui.json
@@ -598,7 +598,8 @@
 # Notes:  An empty password in this command will set the password to the empty
 #         string.  Existing clients are unaffected by executing this command.
 ##
-{ 'command': 'change-vnc-password', 'data': {'password': 'str'},
+{ 'command': 'change-vnc-password',
+  'data': { 'password': 'str' },
   'if': 'defined(CONFIG_VNC)' }
 
 ##
-- 
2.20.0.rc1

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

* [Qemu-devel] [PATCH for-4.0 v7 26/27] qapi: add more conditions to SPICE
  2018-12-08 11:15 [Qemu-devel] [PATCH for-4.0 v7 00/27] Hi, Marc-André Lureau
                   ` (24 preceding siblings ...)
  2018-12-08 11:16 ` [Qemu-devel] [PATCH for-4.0 v7 25/27] qapi: break long lines at 'data' member Marc-André Lureau
@ 2018-12-08 11:16 ` Marc-André Lureau
  2018-12-11 17:11   ` Markus Armbruster
  2018-12-08 11:16 ` [Qemu-devel] [PATCH for-4.0 v7 27/27] qapi: add conditions to REPLICATION type/commands on the schema Marc-André Lureau
                   ` (2 subsequent siblings)
  28 siblings, 1 reply; 69+ messages in thread
From: Marc-André Lureau @ 2018-12-08 11:16 UTC (permalink / raw)
  To: qemu-devel; +Cc: armbru, Marc-André Lureau, Eric Blake, Paolo Bonzini

Now that member can be made conditional, let's make SPICE chardev
conditional:

* spiceport, spicevmc

  Before and after the patch for !CONFIG_SPICE, the error is the
  same ('spiceport' is not a valid char driver name).

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

diff --git a/qapi/char.json b/qapi/char.json
index 24628331ec..77ed847972 100644
--- a/qapi/char.json
+++ b/qapi/char.json
@@ -332,8 +332,8 @@
 ##
 { 'struct': 'ChardevSpiceChannel',
   'data': { 'type': 'str' },
-  'base': 'ChardevCommon' }
-# TODO: 'if': 'defined(CONFIG_SPICE)'
+  'base': 'ChardevCommon',
+  'if': 'defined(CONFIG_SPICE)' }
 
 ##
 # @ChardevSpicePort:
@@ -346,8 +346,8 @@
 ##
 { 'struct': 'ChardevSpicePort',
   'data': { 'fqdn': 'str' },
-  'base': 'ChardevCommon' }
-# TODO: 'if': 'defined(CONFIG_SPICE)'
+  'base': 'ChardevCommon',
+  'if': 'defined(CONFIG_SPICE)' }
 
 ##
 # @ChardevVC:
@@ -404,10 +404,10 @@
             'testdev': 'ChardevCommon',
             'stdio': 'ChardevStdio',
             'console': 'ChardevCommon',
-            'spicevmc': 'ChardevSpiceChannel',
-# TODO: { 'type': 'ChardevSpiceChannel', 'if': 'defined(CONFIG_SPICE)' },
-            'spiceport': 'ChardevSpicePort',
-# TODO: { 'type': 'ChardevSpicePort', 'if': 'defined(CONFIG_SPICE)' },
+            'spicevmc': { 'type': 'ChardevSpiceChannel',
+                          'if': 'defined(CONFIG_SPICE)' },
+            'spiceport': { 'type': 'ChardevSpicePort',
+                           'if': 'defined(CONFIG_SPICE)' },
             'vc': 'ChardevVC',
             'ringbuf': 'ChardevRingbuf',
             # next one is just for compatibility
-- 
2.20.0.rc1

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

* [Qemu-devel] [PATCH for-4.0 v7 27/27] qapi: add conditions to REPLICATION type/commands on the schema
  2018-12-08 11:15 [Qemu-devel] [PATCH for-4.0 v7 00/27] Hi, Marc-André Lureau
                   ` (25 preceding siblings ...)
  2018-12-08 11:16 ` [Qemu-devel] [PATCH for-4.0 v7 26/27] qapi: add more conditions to SPICE Marc-André Lureau
@ 2018-12-08 11:16 ` Marc-André Lureau
  2018-12-11 17:15   ` Markus Armbruster
  2018-12-08 11:19 ` [Qemu-devel] [PATCH for-4.0 v7 00/27] Hi, Marc-André Lureau
  2018-12-12 17:18 ` Markus Armbruster
  28 siblings, 1 reply; 69+ messages in thread
From: Marc-André Lureau @ 2018-12-08 11:16 UTC (permalink / raw)
  To: qemu-devel
  Cc: armbru, Marc-André Lureau, zhanghailiang, Juan Quintela,
	Dr. David Alan Gilbert, Eric Blake

Add #if defined(CONFIG_REPLICATION) in generated code, and adjust the
code accordingly.

Made conditional:

* xen-set-replication, query-xen-replication-status,
  xen-colo-do-checkpoint

  Before the patch, we first register the commands unconditionally in
  generated code (requires a stub), then conditionally unregister in
  qmp_unregister_commands_hack().

  Afterwards, we register only when CONFIG_REPLICATION.  The command
  fails exactly the same, with CommandNotFound.

  Improvement, because now query-qmp-schema is accurate, and we're one
  step closer to killing qmp_unregister_commands_hack().

* enum BlockdevDriver value "replication" in command blockdev-add

* BlockdevOptions variant @replication

and related structures.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
---
 qapi/block-core.json | 13 +++++++++----
 qapi/migration.json  | 12 ++++++++----
 migration/colo.c     | 16 ++++------------
 monitor.c            |  5 -----
 4 files changed, 21 insertions(+), 25 deletions(-)

diff --git a/qapi/block-core.json b/qapi/block-core.json
index 92e0205d91..762000f31f 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -2623,7 +2623,9 @@
             'copy-on-read', 'dmg', 'file', 'ftp', 'ftps', 'gluster',
             'host_cdrom', 'host_device', 'http', 'https', 'iscsi', 'luks',
             'nbd', 'nfs', 'null-aio', 'null-co', 'nvme', 'parallels', 'qcow',
-            'qcow2', 'qed', 'quorum', 'raw', 'rbd', 'replication', 'sheepdog',
+            'qcow2', 'qed', 'quorum', 'raw', 'rbd',
+            { 'name': 'replication', 'if': 'defined(CONFIG_REPLICATION)' },
+            'sheepdog',
             'ssh', 'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] }
 
 ##
@@ -3380,7 +3382,8 @@
 #
 # Since: 2.9
 ##
-{ 'enum' : 'ReplicationMode', 'data' : [ 'primary', 'secondary' ] }
+{ 'enum' : 'ReplicationMode', 'data' : [ 'primary', 'secondary' ],
+  'if': 'defined(CONFIG_REPLICATION)' }
 
 ##
 # @BlockdevOptionsReplication:
@@ -3398,7 +3401,8 @@
 { 'struct': 'BlockdevOptionsReplication',
   'base': 'BlockdevOptionsGenericFormat',
   'data': { 'mode': 'ReplicationMode',
-            '*top-id': 'str' } }
+            '*top-id': 'str' },
+  'if': 'defined(CONFIG_REPLICATION)' }
 
 ##
 # @NFSTransport:
@@ -3714,7 +3718,8 @@
       'quorum':     'BlockdevOptionsQuorum',
       'raw':        'BlockdevOptionsRaw',
       'rbd':        'BlockdevOptionsRbd',
-      'replication':'BlockdevOptionsReplication',
+      'replication': { 'type': 'BlockdevOptionsReplication',
+                       'if': 'defined(CONFIG_REPLICATION)' },
       'sheepdog':   'BlockdevOptionsSheepdog',
       'ssh':        'BlockdevOptionsSsh',
       'throttle':   'BlockdevOptionsThrottle',
diff --git a/qapi/migration.json b/qapi/migration.json
index 5fd33316a0..31b589ec26 100644
--- a/qapi/migration.json
+++ b/qapi/migration.json
@@ -1257,7 +1257,8 @@
 # Since: 2.9
 ##
 { 'command': 'xen-set-replication',
-  'data': { 'enable': 'bool', 'primary': 'bool', '*failover' : 'bool' } }
+  'data': { 'enable': 'bool', 'primary': 'bool', '*failover' : 'bool' },
+  'if': 'defined(CONFIG_REPLICATION)' }
 
 ##
 # @ReplicationStatus:
@@ -1272,7 +1273,8 @@
 # Since: 2.9
 ##
 { 'struct': 'ReplicationStatus',
-  'data': { 'error': 'bool', '*desc': 'str' } }
+  'data': { 'error': 'bool', '*desc': 'str' },
+  'if': 'defined(CONFIG_REPLICATION)' }
 
 ##
 # @query-xen-replication-status:
@@ -1289,7 +1291,8 @@
 # Since: 2.9
 ##
 { 'command': 'query-xen-replication-status',
-  'returns': 'ReplicationStatus' }
+  'returns': 'ReplicationStatus',
+  'if': 'defined(CONFIG_REPLICATION)' }
 
 ##
 # @xen-colo-do-checkpoint:
@@ -1305,7 +1308,8 @@
 #
 # Since: 2.9
 ##
-{ 'command': 'xen-colo-do-checkpoint' }
+{ 'command': 'xen-colo-do-checkpoint',
+  'if': 'defined(CONFIG_REPLICATION)' }
 
 ##
 # @COLOStatus:
diff --git a/migration/colo.c b/migration/colo.c
index fcff04c78c..398b239d1c 100644
--- a/migration/colo.c
+++ b/migration/colo.c
@@ -24,7 +24,9 @@
 #include "trace.h"
 #include "qemu/error-report.h"
 #include "migration/failover.h"
+#ifdef CONFIG_REPLICATION
 #include "replication.h"
+#endif
 #include "net/colo-compare.h"
 #include "net/colo.h"
 #include "block/block.h"
@@ -201,11 +203,11 @@ void colo_do_failover(MigrationState *s)
     }
 }
 
+#ifdef CONFIG_REPLICATION
 void qmp_xen_set_replication(bool enable, bool primary,
                              bool has_failover, bool failover,
                              Error **errp)
 {
-#ifdef CONFIG_REPLICATION
     ReplicationMode mode = primary ?
                            REPLICATION_MODE_PRIMARY :
                            REPLICATION_MODE_SECONDARY;
@@ -224,14 +226,10 @@ void qmp_xen_set_replication(bool enable, bool primary,
         }
         replication_stop_all(failover, failover ? NULL : errp);
     }
-#else
-    abort();
-#endif
 }
 
 ReplicationStatus *qmp_query_xen_replication_status(Error **errp)
 {
-#ifdef CONFIG_REPLICATION
     Error *err = NULL;
     ReplicationStatus *s = g_new0(ReplicationStatus, 1);
 
@@ -246,19 +244,13 @@ ReplicationStatus *qmp_query_xen_replication_status(Error **errp)
 
     error_free(err);
     return s;
-#else
-    abort();
-#endif
 }
 
 void qmp_xen_colo_do_checkpoint(Error **errp)
 {
-#ifdef CONFIG_REPLICATION
     replication_do_checkpoint_all(errp);
-#else
-    abort();
-#endif
 }
+#endif
 
 COLOStatus *qmp_query_colo_status(Error **errp)
 {
diff --git a/monitor.c b/monitor.c
index d39390c2f2..16a180dd50 100644
--- a/monitor.c
+++ b/monitor.c
@@ -1146,11 +1146,6 @@ static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data,
  */
 static void qmp_unregister_commands_hack(void)
 {
-#ifndef CONFIG_REPLICATION
-    qmp_unregister_command(&qmp_commands, "xen-set-replication");
-    qmp_unregister_command(&qmp_commands, "query-xen-replication-status");
-    qmp_unregister_command(&qmp_commands, "xen-colo-do-checkpoint");
-#endif
 #ifndef TARGET_I386
     qmp_unregister_command(&qmp_commands, "rtc-reset-reinjection");
     qmp_unregister_command(&qmp_commands, "query-sev");
-- 
2.20.0.rc1

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

* Re: [Qemu-devel] [PATCH for-4.0 v7 00/27] Hi,
  2018-12-08 11:15 [Qemu-devel] [PATCH for-4.0 v7 00/27] Hi, Marc-André Lureau
                   ` (26 preceding siblings ...)
  2018-12-08 11:16 ` [Qemu-devel] [PATCH for-4.0 v7 27/27] qapi: add conditions to REPLICATION type/commands on the schema Marc-André Lureau
@ 2018-12-08 11:19 ` Marc-André Lureau
  2018-12-12 17:18 ` Markus Armbruster
  28 siblings, 0 replies; 69+ messages in thread
From: Marc-André Lureau @ 2018-12-08 11:19 UTC (permalink / raw)
  To: qemu-devel; +Cc: Armbruster, Markus

Hi,

The cover letter title should be "qapi: add #if pre-processor
conditions to generated code (part 2)"


On Sat, Dec 8, 2018 at 3:16 PM Marc-André Lureau
<marcandre.lureau@redhat.com> wrote:
>
> This is the second part of the "add #if pre-processor conditions to
> generated code" series, adding schema member conditions (roughly
> 16-38/49).
>
> Members can be exploded as dictionnary with 'type'/'if' keys:
>
> { 'struct': 'TestIfStruct', 'data':
>   { 'foo': 'int',
>     'bar': { 'type': 'int', 'if': 'defined(TEST_IF_STRUCT_BAR)'} } }
>
> Enum values can be exploded as dictionnary with 'type'/'if' keys:
>
> { 'enum': 'TestIfEnum', 'data':
>   [ 'foo',
>     { 'name' : 'bar', 'if': 'defined(TEST_IF_ENUM_BAR)' } ] }
>
> v7:
> - replaced osdep.h include by a #warning check
> - renamed event_names/event_enum_members, enum_name/event_enum_ename
> - move normalization after checking
> - simplify make_enum_members()
> - some tests changes
> - rebase/conflicts tweaks
> - squash documentation changes in related patches
> - tweak generated documentation code & output
> - modify commit comments, add rb tags
>
> v6:
> - subset of v5 series: add schema member conditions
> - split "qapi: factor out check_known_keys()", error report improvements
> - add a patch to "qapi: include osdep.h in type headers" to avoid
>   potential build configuration change issues
> - rebased, on top of Markus's qapi-next branch (first 4 patches,
>   included for patchew testing)
>
> Marc-André Lureau (27):
>   qapi: make sure osdep.h is included in type headers
>   qapi: do not define enumeration value explicitly
>   qapi: rename QAPISchemaEnumType.values to .members
>   qapi: change enum visitor and gen_enum* to take QAPISchemaMember
>   tests: print enum type members more like object type members
>   qapi: factor out checking for keys
>   qapi: improve reporting of unknown or missing keys
>   qapi: add a dictionary form with 'name' key for enum members
>   qapi: add 'if' to enum members
>   qapi-events: add 'if' condition to implicit event enum
>   qapi: pass long form enum to make_enum_members
>   qapi: rename allow_dict to allow_implicit
>   qapi: add a dictionary form for TYPE
>   qapi: add 'if' to implicit struct members
>   qapi: add an error in case a discriminator is conditional
>   qapi: add 'if' to union members
>   qapi: simplify make_enum_members()
>   tests/qapi: add command with condition on union argument
>   qapi: add 'if' to alternate members
>   tests/qapi: add command with condition on alternate argument
>   qapi: add #if conditions to generated code members
>   qapi: add 'If:' condition to enum values documentation
>   qapi: add 'If:' condition to struct members documentation
>   qapi: add condition to variants documentation
>   qapi: break long lines at 'data' member
>   qapi: add more conditions to SPICE
>   qapi: add conditions to REPLICATION type/commands on the schema
>
>  qapi/block-core.json                          |  26 +-
>  qapi/char.json                                | 150 ++++++-----
>  qapi/migration.json                           |  15 +-
>  qapi/misc.json                                |   7 +-
>  qapi/net.json                                 |   3 +-
>  qapi/tpm.json                                 |   5 +-
>  qapi/ui.json                                  |   3 +-
>  scripts/qapi/common.py                        | 253 ++++++++++++------
>  scripts/qapi/doc.py                           |  30 ++-
>  scripts/qapi/events.py                        |  13 +-
>  scripts/qapi/introspect.py                    |  16 +-
>  scripts/qapi/types.py                         |  13 +-
>  scripts/qapi/visit.py                         |   8 +-
>  migration/colo.c                              |  16 +-
>  monitor.c                                     |   5 -
>  docs/devel/qapi-code-gen.txt                  |  19 ++
>  tests/Makefile.include                        |  11 +-
>  tests/qapi-schema/alternate-base.err          |   1 +
>  tests/qapi-schema/alternate-invalid-dict.err  |   1 +
>  ...ember.exit => alternate-invalid-dict.exit} |   0
>  tests/qapi-schema/alternate-invalid-dict.json |   4 +
>  ...-member.out => alternate-invalid-dict.out} |   0
>  tests/qapi-schema/comments.out                |  14 +-
>  tests/qapi-schema/doc-bad-section.out         |  13 +-
>  tests/qapi-schema/doc-good.json               |  11 +-
>  tests/qapi-schema/doc-good.out                |  22 +-
>  tests/qapi-schema/doc-good.texi               |   7 +-
>  tests/qapi-schema/double-type.err             |   1 +
>  tests/qapi-schema/empty.out                   |   9 +-
>  tests/qapi-schema/enum-bad-member.err         |   1 +
>  tests/qapi-schema/enum-bad-member.exit        |   1 +
>  tests/qapi-schema/enum-bad-member.json        |   2 +
>  tests/qapi-schema/enum-bad-member.out         |   0
>  .../qapi-schema/enum-dict-member-unknown.err  |   2 +
>  .../qapi-schema/enum-dict-member-unknown.exit |   1 +
>  .../qapi-schema/enum-dict-member-unknown.json |   2 +
>  .../qapi-schema/enum-dict-member-unknown.out  |   0
>  tests/qapi-schema/enum-dict-member.err        |   1 -
>  tests/qapi-schema/enum-dict-member.json       |   2 -
>  tests/qapi-schema/enum-if-invalid.err         |   1 +
>  tests/qapi-schema/enum-if-invalid.exit        |   1 +
>  tests/qapi-schema/enum-if-invalid.json        |   3 +
>  tests/qapi-schema/enum-if-invalid.out         |   0
>  tests/qapi-schema/event-case.out              |   9 +-
>  .../qapi-schema/event-member-invalid-dict.err |   1 +
>  .../event-member-invalid-dict.exit            |   1 +
>  .../event-member-invalid-dict.json            |   2 +
>  .../qapi-schema/event-member-invalid-dict.out |   0
>  tests/qapi-schema/event-nest-struct.json      |   2 +-
>  .../flat-union-inline-invalid-dict.err        |   1 +
>  .../flat-union-inline-invalid-dict.exit       |   1 +
>  .../flat-union-inline-invalid-dict.json       |  11 +
>  .../flat-union-inline-invalid-dict.out        |   0
>  tests/qapi-schema/flat-union-inline.json      |   2 +-
>  .../flat-union-invalid-if-discriminator.err   |   1 +
>  .../flat-union-invalid-if-discriminator.exit  |   1 +
>  .../flat-union-invalid-if-discriminator.json  |  17 ++
>  .../flat-union-invalid-if-discriminator.out   |   0
>  tests/qapi-schema/ident-with-escape.out       |   9 +-
>  tests/qapi-schema/include-relpath.out         |  14 +-
>  tests/qapi-schema/include-repetition.out      |  14 +-
>  tests/qapi-schema/include-simple.out          |  14 +-
>  tests/qapi-schema/indented-expr.out           |   9 +-
>  .../nested-struct-data-invalid-dict.err       |   1 +
>  .../nested-struct-data-invalid-dict.exit      |   1 +
>  .../nested-struct-data-invalid-dict.json      |   3 +
>  .../nested-struct-data-invalid-dict.out       |   0
>  tests/qapi-schema/nested-struct-data.json     |   2 +-
>  tests/qapi-schema/qapi-schema-test.json       |  39 ++-
>  tests/qapi-schema/qapi-schema-test.out        |  74 ++++-
>  .../struct-member-invalid-dict.err            |   1 +
>  .../struct-member-invalid-dict.exit           |   1 +
>  .../struct-member-invalid-dict.json           |   3 +
>  .../struct-member-invalid-dict.out            |   0
>  tests/qapi-schema/test-qapi.py                |   9 +-
>  .../qapi-schema/union-branch-invalid-dict.err |   1 +
>  .../union-branch-invalid-dict.exit            |   1 +
>  .../union-branch-invalid-dict.json            |   4 +
>  .../qapi-schema/union-branch-invalid-dict.out |   0
>  tests/qapi-schema/unknown-expr-key.err        |   3 +-
>  tests/qapi-schema/unknown-expr-key.json       |   2 +-
>  81 files changed, 681 insertions(+), 266 deletions(-)
>  create mode 100644 tests/qapi-schema/alternate-invalid-dict.err
>  rename tests/qapi-schema/{enum-dict-member.exit => alternate-invalid-dict.exit} (100%)
>  create mode 100644 tests/qapi-schema/alternate-invalid-dict.json
>  rename tests/qapi-schema/{enum-dict-member.out => alternate-invalid-dict.out} (100%)
>  create mode 100644 tests/qapi-schema/enum-bad-member.err
>  create mode 100644 tests/qapi-schema/enum-bad-member.exit
>  create mode 100644 tests/qapi-schema/enum-bad-member.json
>  create mode 100644 tests/qapi-schema/enum-bad-member.out
>  create mode 100644 tests/qapi-schema/enum-dict-member-unknown.err
>  create mode 100644 tests/qapi-schema/enum-dict-member-unknown.exit
>  create mode 100644 tests/qapi-schema/enum-dict-member-unknown.json
>  create mode 100644 tests/qapi-schema/enum-dict-member-unknown.out
>  delete mode 100644 tests/qapi-schema/enum-dict-member.err
>  delete mode 100644 tests/qapi-schema/enum-dict-member.json
>  create mode 100644 tests/qapi-schema/enum-if-invalid.err
>  create mode 100644 tests/qapi-schema/enum-if-invalid.exit
>  create mode 100644 tests/qapi-schema/enum-if-invalid.json
>  create mode 100644 tests/qapi-schema/enum-if-invalid.out
>  create mode 100644 tests/qapi-schema/event-member-invalid-dict.err
>  create mode 100644 tests/qapi-schema/event-member-invalid-dict.exit
>  create mode 100644 tests/qapi-schema/event-member-invalid-dict.json
>  create mode 100644 tests/qapi-schema/event-member-invalid-dict.out
>  create mode 100644 tests/qapi-schema/flat-union-inline-invalid-dict.err
>  create mode 100644 tests/qapi-schema/flat-union-inline-invalid-dict.exit
>  create mode 100644 tests/qapi-schema/flat-union-inline-invalid-dict.json
>  create mode 100644 tests/qapi-schema/flat-union-inline-invalid-dict.out
>  create mode 100644 tests/qapi-schema/flat-union-invalid-if-discriminator.err
>  create mode 100644 tests/qapi-schema/flat-union-invalid-if-discriminator.exit
>  create mode 100644 tests/qapi-schema/flat-union-invalid-if-discriminator.json
>  create mode 100644 tests/qapi-schema/flat-union-invalid-if-discriminator.out
>  create mode 100644 tests/qapi-schema/nested-struct-data-invalid-dict.err
>  create mode 100644 tests/qapi-schema/nested-struct-data-invalid-dict.exit
>  create mode 100644 tests/qapi-schema/nested-struct-data-invalid-dict.json
>  create mode 100644 tests/qapi-schema/nested-struct-data-invalid-dict.out
>  create mode 100644 tests/qapi-schema/struct-member-invalid-dict.err
>  create mode 100644 tests/qapi-schema/struct-member-invalid-dict.exit
>  create mode 100644 tests/qapi-schema/struct-member-invalid-dict.json
>  create mode 100644 tests/qapi-schema/struct-member-invalid-dict.out
>  create mode 100644 tests/qapi-schema/union-branch-invalid-dict.err
>  create mode 100644 tests/qapi-schema/union-branch-invalid-dict.exit
>  create mode 100644 tests/qapi-schema/union-branch-invalid-dict.json
>  create mode 100644 tests/qapi-schema/union-branch-invalid-dict.out
>
> --
> 2.20.0.rc1
>

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

* Re: [Qemu-devel] [PATCH for-4.0 v7 01/27] qapi: make sure osdep.h is included in type headers
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 01/27] qapi: make sure osdep.h is included in type headers Marc-André Lureau
@ 2018-12-10  9:52   ` Markus Armbruster
  2018-12-10 11:13     ` Marc-André Lureau
  0 siblings, 1 reply; 69+ messages in thread
From: Markus Armbruster @ 2018-12-10  9:52 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, Michael Roth

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

> Now that the schema can be configured, it is crucial that all types
> are configured the same. Make sure config-host.h is included, by
> checking osdep.h inclusion. The build-sys tracks the dependency and
> rebuilds the types if the configuration changed.
>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  scripts/qapi/types.py | 3 +++
>  1 file changed, 3 insertions(+)
>
> diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py
> index fd7808103c..3bb9cb6d47 100644
> --- a/scripts/qapi/types.py
> +++ b/scripts/qapi/types.py
> @@ -201,6 +201,9 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor):
>  ''',
>                                        types=types, visit=visit))
>          self._genh.preamble_add(mcgen('''
> +#ifndef QEMU_OSDEP_H
> +#error Please include osdep.h first!
> +#endif
>  #include "qapi/qapi-builtin-types.h"
>  '''))

I understand why you propose this patch.  The QAPI-generated headers use
#ifdef CONFIG_FOO.  The configuration header "qemu/osdep.h" must be
consistently included before the generated headers, or else you end up
with nasty bugs, such as the same enum having different values in
different .o, or the same struct having a different layout.

But this applies to *all* headers that use #ifdef.  Why check it here,
but not there?  What makes the QAPI-generated headers special?

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

* Re: [Qemu-devel] [PATCH for-4.0 v7 06/27] qapi: factor out checking for keys
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 06/27] qapi: factor out checking for keys Marc-André Lureau
@ 2018-12-10  9:57   ` Markus Armbruster
  0 siblings, 0 replies; 69+ messages in thread
From: Markus Armbruster @ 2018-12-10  9:57 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, Michael Roth

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

> Introduce a new helper function to check if the given keys are known,
> and if mandatory keys are present. The function will be reused in
> other places in the following code changes.
>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

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

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

* Re: [Qemu-devel] [PATCH for-4.0 v7 07/27] qapi: improve reporting of unknown or missing keys
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 07/27] qapi: improve reporting of unknown or missing keys Marc-André Lureau
@ 2018-12-10 10:03   ` Markus Armbruster
  2018-12-10 11:11     ` Marc-André Lureau
  0 siblings, 1 reply; 69+ messages in thread
From: Markus Armbruster @ 2018-12-10 10:03 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, Michael Roth

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

> Report the set of missing or unknown keys. And give a hint about the
> accepted keys.

Suggest to add:

  The error message for multiple meta type members (visible in
  tests/qapi-schema/double-type.err) is not improved.

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

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

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

* Re: [Qemu-devel] [PATCH for-4.0 v7 07/27] qapi: improve reporting of unknown or missing keys
  2018-12-10 10:03   ` Markus Armbruster
@ 2018-12-10 11:11     ` Marc-André Lureau
  0 siblings, 0 replies; 69+ messages in thread
From: Marc-André Lureau @ 2018-12-10 11:11 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: QEMU, Michael Roth

On Mon, Dec 10, 2018 at 2:03 PM Markus Armbruster <armbru@redhat.com> wrote:
>
> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
>
> > Report the set of missing or unknown keys. And give a hint about the
> > accepted keys.
>
> Suggest to add:
>
>   The error message for multiple meta type members (visible in
>   tests/qapi-schema/double-type.err) is not improved.
>
> > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>
> Reviewed-by: Markus Armbruster <armbru@redhat.com>
>

ack, thanks

-- 
Marc-André Lureau

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

* Re: [Qemu-devel] [PATCH for-4.0 v7 01/27] qapi: make sure osdep.h is included in type headers
  2018-12-10  9:52   ` Markus Armbruster
@ 2018-12-10 11:13     ` Marc-André Lureau
  2018-12-10 13:28       ` Markus Armbruster
  0 siblings, 1 reply; 69+ messages in thread
From: Marc-André Lureau @ 2018-12-10 11:13 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: QEMU, Michael Roth

Hi

On Mon, Dec 10, 2018 at 1:52 PM Markus Armbruster <armbru@redhat.com> wrote:
>
> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
>
> > Now that the schema can be configured, it is crucial that all types
> > are configured the same. Make sure config-host.h is included, by
> > checking osdep.h inclusion. The build-sys tracks the dependency and
> > rebuilds the types if the configuration changed.
> >
> > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> > ---
> >  scripts/qapi/types.py | 3 +++
> >  1 file changed, 3 insertions(+)
> >
> > diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py
> > index fd7808103c..3bb9cb6d47 100644
> > --- a/scripts/qapi/types.py
> > +++ b/scripts/qapi/types.py
> > @@ -201,6 +201,9 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor):
> >  ''',
> >                                        types=types, visit=visit))
> >          self._genh.preamble_add(mcgen('''
> > +#ifndef QEMU_OSDEP_H
> > +#error Please include osdep.h first!
> > +#endif
> >  #include "qapi/qapi-builtin-types.h"
> >  '''))
>
> I understand why you propose this patch.  The QAPI-generated headers use
> #ifdef CONFIG_FOO.  The configuration header "qemu/osdep.h" must be
> consistently included before the generated headers, or else you end up
> with nasty bugs, such as the same enum having different values in
> different .o, or the same struct having a different layout.
>
> But this applies to *all* headers that use #ifdef.  Why check it here,
> but not there?  What makes the QAPI-generated headers special?
>

It's the discussion about #if in headers (and enums in particular)
that started this. We want to make sure all compilation units share
the same data structure/ABI. I proposed to include osdep.h in qapi
headers, but that was rejected.
The warning is a different approach. I agree it could apply to all
headers. Do you think I should update all headers as well?


-- 
Marc-André Lureau

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

* Re: [Qemu-devel] [PATCH for-4.0 v7 01/27] qapi: make sure osdep.h is included in type headers
  2018-12-10 11:13     ` Marc-André Lureau
@ 2018-12-10 13:28       ` Markus Armbruster
  2018-12-11 15:47         ` Marc-André Lureau
  2018-12-11 16:05         ` Daniel P. Berrangé
  0 siblings, 2 replies; 69+ messages in thread
From: Markus Armbruster @ 2018-12-10 13:28 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: QEMU, Michael Roth

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

> Hi
>
> On Mon, Dec 10, 2018 at 1:52 PM Markus Armbruster <armbru@redhat.com> wrote:
>>
>> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
>>
>> > Now that the schema can be configured, it is crucial that all types
>> > are configured the same. Make sure config-host.h is included, by
>> > checking osdep.h inclusion. The build-sys tracks the dependency and
>> > rebuilds the types if the configuration changed.
>> >
>> > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>> > ---
>> >  scripts/qapi/types.py | 3 +++
>> >  1 file changed, 3 insertions(+)
>> >
>> > diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py
>> > index fd7808103c..3bb9cb6d47 100644
>> > --- a/scripts/qapi/types.py
>> > +++ b/scripts/qapi/types.py
>> > @@ -201,6 +201,9 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor):
>> >  ''',
>> >                                        types=types, visit=visit))
>> >          self._genh.preamble_add(mcgen('''
>> > +#ifndef QEMU_OSDEP_H
>> > +#error Please include osdep.h first!
>> > +#endif
>> >  #include "qapi/qapi-builtin-types.h"
>> >  '''))
>>
>> I understand why you propose this patch.  The QAPI-generated headers use
>> #ifdef CONFIG_FOO.  The configuration header "qemu/osdep.h" must be
>> consistently included before the generated headers, or else you end up
>> with nasty bugs, such as the same enum having different values in
>> different .o, or the same struct having a different layout.
>>
>> But this applies to *all* headers that use #ifdef.  Why check it here,
>> but not there?  What makes the QAPI-generated headers special?
>>
>
> It's the discussion about #if in headers (and enums in particular)
> that started this. We want to make sure all compilation units share
> the same data structure/ABI. I proposed to include osdep.h in qapi
> headers, but that was rejected.
> The warning is a different approach. I agree it could apply to all
> headers. Do you think I should update all headers as well?

That would replace the rule 'all .c files must include "qemu/osdep.h"
first' by 'all .h files must check "qemu/osdep.h" has been included
already', which is not an improvement.

All we have right now to help with enforcing our osdep.h rule is
scripts/clean-includes.  We run it periodically to fix rule breakers.
It's manual, because we have a few .c files in the tree where the rule
doesn't apply, and the script doesn't know about them.  Fixable, I
guess.  Most recent run:

    Subject: [PATCH] Clean up includes
    Message-Id: <20181204172535.2799-1-armbru@redhat.com>
    https://lists.nongnu.org/archive/html/qemu-devel/2018-12/msg00605.html

The obvious improvement would be flagging rule breakers before they get
committed.

checkpatch.pl could flag patches adding .c files that don't include
"qemu/osdep.h" first.  The "first" part might be a bit annoying to code.
The "adding files" part already exists: checkpatch.pl warns when you add
a file without updating MAINTAINERS.

checkpatch.pl could also flag patches removing #include "qemu/osdep.h".

Other projects use a syntax-check make target to check sources.  If we
decide we like that better than checkpatch.pl for some checks, we can do
that.

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

* Re: [Qemu-devel] [PATCH for-4.0 v7 08/27] qapi: add a dictionary form with 'name' key for enum members
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 08/27] qapi: add a dictionary form with 'name' key for enum members Marc-André Lureau
@ 2018-12-10 15:44   ` Markus Armbruster
  2018-12-10 16:05     ` Markus Armbruster
  0 siblings, 1 reply; 69+ messages in thread
From: Markus Armbruster @ 2018-12-10 15:44 UTC (permalink / raw)
  Cc: qemu-devel, armbru, Michael Roth

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

> Desugar the enum NAME form to { 'name': NAME }. This will allow to add
> new enum members, such as 'if' in the following patch.
>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  scripts/qapi/common.py                        | 49 ++++++++++++++++---
>  tests/Makefile.include                        |  3 +-
>  tests/qapi-schema/enum-bad-member.err         |  1 +
>  ...-dict-member.exit => enum-bad-member.exit} |  0
>  tests/qapi-schema/enum-bad-member.json        |  2 +
>  ...um-dict-member.out => enum-bad-member.out} |  0
>  .../qapi-schema/enum-dict-member-unknown.err  |  2 +
>  .../qapi-schema/enum-dict-member-unknown.exit |  1 +
>  .../qapi-schema/enum-dict-member-unknown.json |  2 +
>  .../qapi-schema/enum-dict-member-unknown.out  |  0
>  tests/qapi-schema/enum-dict-member.err        |  1 -
>  tests/qapi-schema/enum-dict-member.json       |  2 -
>  12 files changed, 52 insertions(+), 11 deletions(-)
>  create mode 100644 tests/qapi-schema/enum-bad-member.err
>  rename tests/qapi-schema/{enum-dict-member.exit => enum-bad-member.exit} (100%)
>  create mode 100644 tests/qapi-schema/enum-bad-member.json
>  rename tests/qapi-schema/{enum-dict-member.out => enum-bad-member.out} (100%)
>  create mode 100644 tests/qapi-schema/enum-dict-member-unknown.err
>  create mode 100644 tests/qapi-schema/enum-dict-member-unknown.exit
>  create mode 100644 tests/qapi-schema/enum-dict-member-unknown.json
>  create mode 100644 tests/qapi-schema/enum-dict-member-unknown.out
>  delete mode 100644 tests/qapi-schema/enum-dict-member.err
>  delete mode 100644 tests/qapi-schema/enum-dict-member.json
>
> diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
> index f205805751..610840d2e5 100644
> --- a/scripts/qapi/common.py
> +++ b/scripts/qapi/common.py
> @@ -740,6 +740,10 @@ def check_event(expr, info):
>                 allow_metas=meta)
>  
>  
> +def enum_get_names(expr):
> +    return [e['name'] if isinstance(e, dict) else e for e in expr['data']]
> +
> +

Slightly more complicated than v6 because it @expr isn't normalized.
We'll revisit this at [*] below.

Another way to code this would be

       members = normalized_enum_members(e['data'])
       return [m['type'] for m in members]

where

   def normalized_enum_members(members):
       [m if isinstance(m, dict) else {'name': m} for m in members]

which then gets reused in normalize_enum()

   def normalize_enum(expr)
       expr['data'] = normalized_enum_members(expr['data']

Up to you.  We could also change it on top.

>  def check_union(expr, info):
>      name = expr['union']
>      base = expr.get('base')
> @@ -799,7 +803,7 @@ def check_union(expr, info):
>          # If the discriminator names an enum type, then all members
>          # of 'data' must also be members of the enum type.
>          if enum_define:
> -            if key not in enum_define['data']:
> +            if key not in enum_get_names(enum_define):
>                  raise QAPISemError(info,
>                                     "Discriminator value '%s' is not found in "
>                                     "enum '%s'"
> @@ -831,7 +835,7 @@ def check_alternate(expr, info):
>          if qtype == 'QTYPE_QSTRING':
>              enum_expr = enum_types.get(value)
>              if enum_expr:
> -                for v in enum_expr['data']:
> +                for v in enum_get_names(enum_expr):
>                      if v in ['on', 'off']:
>                          conflicting.add('QTYPE_QBOOL')
>                      if re.match(r'[-+0-9.]', v): # lazy, could be tightened
> @@ -847,18 +851,32 @@ def check_alternate(expr, info):
>              types_seen[qt] = key
>  
>  
> +def normalize_enum(expr):
> +    members = expr['data']
> +
> +    # translate short member form to dict form
> +    expr['data'] = [m if isinstance(m, dict) else {'name': m} for m in members]
> +
> +
>  def check_enum(expr, info):
>      name = expr['enum']
> -    members = expr.get('data')
> +    members = expr['data']
>      prefix = expr.get('prefix')
>  
> -    if not isinstance(members, list):
> -        raise QAPISemError(info,
> -                           "Enum '%s' requires an array for 'data'" % name)
>      if prefix is not None and not isinstance(prefix, str):
>          raise QAPISemError(info,
>                             "Enum '%s' requires a string for 'prefix'" % name)
> +
> +    if not isinstance(members, list):
> +        raise QAPISemError(info,
> +                           "Enum '%s' requires an array for 'data'" % name)
> +

Did you move this check down intentionally?

>      for member in members:
> +        if isinstance(member, dict):
> +            source = "dictionary member of enum '%s'" % name
> +            check_known_keys(info, source, member, ['name'], [])
> +            member = member['name']
> +
>          check_name(info, "Member of enum '%s'" % name, member,
>                     enum_member=True)

Again, slightly more complicated than v6 because it now runs before
normalization.  We'll revisit this at [*] below.

>  
> @@ -1011,6 +1029,15 @@ def check_exprs(exprs):
>      return exprs
>  
>  
> +def normalize_exprs(exprs):
> +    for expr_elem in exprs:
> +        expr = expr_elem['expr']
> +        if 'enum' in expr:
> +            normalize_enum(expr)
> +
> +    return exprs
> +
> +
>  #
>  # Schema compiler frontend
>  #
> @@ -1567,6 +1594,7 @@ class QAPISchema(object):
>              f = open(fname, 'r')
>          parser = QAPISchemaParser(f)
>          exprs = check_exprs(parser.exprs)
> +        exprs = normalize_exprs(exprs)
>          self.docs = parser.docs
>          self._entity_list = []
>          self._entity_dict = {}

[*] In v6, you normalized at the beginning of check_exprs().  Now, you
normalize after check_exprs().  Works, although what I suggested in my
review of v6 was to normalize later in check_exprs(), namely right after
check_keys().  I believe that would avoid the slight complications I
noted above.

> @@ -1640,7 +1668,14 @@ class QAPISchema(object):
>                                              qtype_values, 'QTYPE'))
>  
>      def _make_enum_members(self, values):
> -        return [QAPISchemaMember(v) for v in values]
> +        enum = []
> +        for v in values:
> +            if isinstance(v, dict):
> +                name = v['name']
> +            else:
> +                name = v
> +            enum.append(QAPISchemaMember(name))
> +        return enum

Despite normalization to the long form, we still deal with both the long
and the short form here, same as in v6.  I see you addressed this in new
PATCH 11+17.  What's wrong with squashing them into this patch?

>  
>      def _make_implicit_enum_type(self, name, info, ifcond, values):
>          # See also QAPISchemaObjectTypeMember._pretty_owner()
> diff --git a/tests/Makefile.include b/tests/Makefile.include
> index fb0b449c02..2e894c1037 100644
> --- a/tests/Makefile.include
> +++ b/tests/Makefile.include
> @@ -379,10 +379,11 @@ qapi-schema += double-data.json
>  qapi-schema += double-type.json
>  qapi-schema += duplicate-key.json
>  qapi-schema += empty.json
> +qapi-schema += enum-bad-member.json
>  qapi-schema += enum-bad-name.json
>  qapi-schema += enum-bad-prefix.json
>  qapi-schema += enum-clash-member.json
> -qapi-schema += enum-dict-member.json
> +qapi-schema += enum-dict-member-unknown.json
>  qapi-schema += enum-int-member.json
>  qapi-schema += enum-member-case.json
>  qapi-schema += enum-missing-data.json
> diff --git a/tests/qapi-schema/enum-bad-member.err b/tests/qapi-schema/enum-bad-member.err
> new file mode 100644
> index 0000000000..211db9e6fc
> --- /dev/null
> +++ b/tests/qapi-schema/enum-bad-member.err
> @@ -0,0 +1 @@
> +tests/qapi-schema/enum-bad-member.json:2: Member of enum 'MyEnum' requires a string name
> diff --git a/tests/qapi-schema/enum-dict-member.exit b/tests/qapi-schema/enum-bad-member.exit
> similarity index 100%
> rename from tests/qapi-schema/enum-dict-member.exit
> rename to tests/qapi-schema/enum-bad-member.exit
> diff --git a/tests/qapi-schema/enum-bad-member.json b/tests/qapi-schema/enum-bad-member.json
> new file mode 100644
> index 0000000000..98da6828b4
> --- /dev/null
> +++ b/tests/qapi-schema/enum-bad-member.json
> @@ -0,0 +1,2 @@
> +# we reject any enum member that is not a string
> +{ 'enum': 'MyEnum', 'data': [ [ ] ] }
> diff --git a/tests/qapi-schema/enum-dict-member.out b/tests/qapi-schema/enum-bad-member.out
> similarity index 100%
> rename from tests/qapi-schema/enum-dict-member.out
> rename to tests/qapi-schema/enum-bad-member.out
> diff --git a/tests/qapi-schema/enum-dict-member-unknown.err b/tests/qapi-schema/enum-dict-member-unknown.err
> new file mode 100644
> index 0000000000..76bd0471db
> --- /dev/null
> +++ b/tests/qapi-schema/enum-dict-member-unknown.err
> @@ -0,0 +1,2 @@
> +tests/qapi-schema/enum-dict-member-unknown.json:2: Unknown key 'bad-key' in dictionary member of enum 'MyEnum'
> +Valid keys are 'name'.
> diff --git a/tests/qapi-schema/enum-dict-member-unknown.exit b/tests/qapi-schema/enum-dict-member-unknown.exit
> new file mode 100644
> index 0000000000..d00491fd7e
> --- /dev/null
> +++ b/tests/qapi-schema/enum-dict-member-unknown.exit
> @@ -0,0 +1 @@
> +1
> diff --git a/tests/qapi-schema/enum-dict-member-unknown.json b/tests/qapi-schema/enum-dict-member-unknown.json
> new file mode 100644
> index 0000000000..6664c59201
> --- /dev/null
> +++ b/tests/qapi-schema/enum-dict-member-unknown.json
> @@ -0,0 +1,2 @@
> +# we reject any enum member that is not a string or a dict with 'name'
> +{ 'enum': 'MyEnum', 'data': [ { 'name': 'foo', 'bad-key': 'str' } ] }
> diff --git a/tests/qapi-schema/enum-dict-member-unknown.out b/tests/qapi-schema/enum-dict-member-unknown.out
> new file mode 100644
> index 0000000000..e69de29bb2
> diff --git a/tests/qapi-schema/enum-dict-member.err b/tests/qapi-schema/enum-dict-member.err
> deleted file mode 100644
> index 8ca146ea59..0000000000
> --- a/tests/qapi-schema/enum-dict-member.err
> +++ /dev/null
> @@ -1 +0,0 @@
> -tests/qapi-schema/enum-dict-member.json:2: Member of enum 'MyEnum' requires a string name
> diff --git a/tests/qapi-schema/enum-dict-member.json b/tests/qapi-schema/enum-dict-member.json
> deleted file mode 100644
> index 79672e0f09..0000000000
> --- a/tests/qapi-schema/enum-dict-member.json
> +++ /dev/null
> @@ -1,2 +0,0 @@
> -# we reject any enum member that is not a string
> -{ 'enum': 'MyEnum', 'data': [ { 'value': 'str' } ] }

I think we still need this one to cover "key 'name' is required in the
long form".

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

* Re: [Qemu-devel] [PATCH for-4.0 v7 08/27] qapi: add a dictionary form with 'name' key for enum members
  2018-12-10 15:44   ` Markus Armbruster
@ 2018-12-10 16:05     ` Markus Armbruster
  0 siblings, 0 replies; 69+ messages in thread
From: Markus Armbruster @ 2018-12-10 16:05 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, Michael Roth

One more thing...

Markus Armbruster <armbru@redhat.com> writes:

> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
>
>> Desugar the enum NAME form to { 'name': NAME }. This will allow to add
>> new enum members, such as 'if' in the following patch.
>>
>> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>> ---
>>  scripts/qapi/common.py                        | 49 ++++++++++++++++---
>>  tests/Makefile.include                        |  3 +-
>>  tests/qapi-schema/enum-bad-member.err         |  1 +
>>  ...-dict-member.exit => enum-bad-member.exit} |  0
>>  tests/qapi-schema/enum-bad-member.json        |  2 +
>>  ...um-dict-member.out => enum-bad-member.out} |  0
>>  .../qapi-schema/enum-dict-member-unknown.err  |  2 +
>>  .../qapi-schema/enum-dict-member-unknown.exit |  1 +
>>  .../qapi-schema/enum-dict-member-unknown.json |  2 +
>>  .../qapi-schema/enum-dict-member-unknown.out  |  0
>>  tests/qapi-schema/enum-dict-member.err        |  1 -
>>  tests/qapi-schema/enum-dict-member.json       |  2 -
>>  12 files changed, 52 insertions(+), 11 deletions(-)
>>  create mode 100644 tests/qapi-schema/enum-bad-member.err
>>  rename tests/qapi-schema/{enum-dict-member.exit => enum-bad-member.exit} (100%)
>>  create mode 100644 tests/qapi-schema/enum-bad-member.json
>>  rename tests/qapi-schema/{enum-dict-member.out => enum-bad-member.out} (100%)
>>  create mode 100644 tests/qapi-schema/enum-dict-member-unknown.err
>>  create mode 100644 tests/qapi-schema/enum-dict-member-unknown.exit
>>  create mode 100644 tests/qapi-schema/enum-dict-member-unknown.json
>>  create mode 100644 tests/qapi-schema/enum-dict-member-unknown.out
>>  delete mode 100644 tests/qapi-schema/enum-dict-member.err
>>  delete mode 100644 tests/qapi-schema/enum-dict-member.json
>>
>> diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
[...]
>> @@ -847,18 +851,32 @@ def check_alternate(expr, info):
>>              types_seen[qt] = key
>>  
>>  
>> +def normalize_enum(expr):
>> +    members = expr['data']
>> +
>> +    # translate short member form to dict form
>> +    expr['data'] = [m if isinstance(m, dict) else {'name': m} for m in members]
>> +
>> +
>>  def check_enum(expr, info):
>>      name = expr['enum']
>> -    members = expr.get('data')
>> +    members = expr['data']
>>      prefix = expr.get('prefix')
>>  
>> -    if not isinstance(members, list):
>> -        raise QAPISemError(info,
>> -                           "Enum '%s' requires an array for 'data'" % name)
>>      if prefix is not None and not isinstance(prefix, str):
>>          raise QAPISemError(info,
>>                             "Enum '%s' requires a string for 'prefix'" % name)
>> +
>> +    if not isinstance(members, list):
>> +        raise QAPISemError(info,
>> +                           "Enum '%s' requires an array for 'data'" % name)
>> +
>
> Did you move this check down intentionally?
>
>>      for member in members:
>> +        if isinstance(member, dict):
>> +            source = "dictionary member of enum '%s'" % name
>> +            check_known_keys(info, source, member, ['name'], [])
>> +            member = member['name']
>> +
>>          check_name(info, "Member of enum '%s'" % name, member,
>>                     enum_member=True)
>
> Again, slightly more complicated than v6 because it now runs before
> normalization.  We'll revisit this at [*] below.

If we keep it this way, then let's at least avoid assigning to the loop
control variable:

                member_name = member['name']
            else:
                member_name = member
            check_name(info, "Member of enum '%s'" % name, member_name,
                       enum_member=True)

[...]

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

* Re: [Qemu-devel] [PATCH for-4.0 v7 11/27] qapi: pass long form enum to make_enum_members
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 11/27] qapi: pass long form enum to make_enum_members Marc-André Lureau
@ 2018-12-10 17:04   ` Markus Armbruster
  0 siblings, 0 replies; 69+ messages in thread
From: Markus Armbruster @ 2018-12-10 17:04 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, Michael Roth

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

> This will allow to get rid of short form handling in a following
> patch.
>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> Suggested-by: Markus Armbruster <armbru@redhat.com>
> ---
>  scripts/qapi/common.py | 7 ++++---
>  1 file changed, 4 insertions(+), 3 deletions(-)
>
> diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
> index fdc0fd69ef..557b413950 100644
> --- a/scripts/qapi/common.py
> +++ b/scripts/qapi/common.py
> @@ -1663,9 +1663,10 @@ class QAPISchema(object):
>          self.the_empty_object_type = QAPISchemaObjectType(
>              'q_empty', None, None, None, None, [], None)
>          self._def_entity(self.the_empty_object_type)
> -        qtype_values = self._make_enum_members(['none', 'qnull', 'qnum',
> -                                                'qstring', 'qdict', 'qlist',
> -                                                'qbool'])
> +
> +        qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']

Long line.

> +        qtype_values = self._make_enum_members([{'name': n} for n in qtypes])
> +
>          self._def_entity(QAPISchemaEnumType('QType', None, None, None,
>                                              qtype_values, 'QTYPE'))

We may want to squash this one into PATCH 08 as mentioned in that
patch's review.

In case we don't:
Reviewed-by: Markus Armbruster <armbru@redhat.com>

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

* Re: [Qemu-devel] [PATCH for-4.0 v7 13/27] qapi: add a dictionary form for TYPE
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 13/27] qapi: add a dictionary form for TYPE Marc-André Lureau
@ 2018-12-10 17:24   ` Markus Armbruster
  2018-12-11 12:11     ` Marc-André Lureau
  2018-12-11 15:18     ` Markus Armbruster
  0 siblings, 2 replies; 69+ messages in thread
From: Markus Armbruster @ 2018-12-10 17:24 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, Michael Roth

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

> Wherever a struct/union/alternate/command/event member with NAME: TYPE
> form is accepted, desugar it to a NAME: { 'type': TYPE } form.
>
> This will allow to add new member details, such as 'if' in the
> following patch to introduce conditionals, or 'default' for default
> values etc.
>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  scripts/qapi/common.py                        | 71 ++++++++++++++-----
>  tests/Makefile.include                        |  6 ++
>  tests/qapi-schema/alternate-invalid-dict.err  |  1 +
>  tests/qapi-schema/alternate-invalid-dict.exit |  1 +
>  tests/qapi-schema/alternate-invalid-dict.json |  4 ++
>  tests/qapi-schema/alternate-invalid-dict.out  |  0
>  .../qapi-schema/event-member-invalid-dict.err |  1 +
>  .../event-member-invalid-dict.exit            |  1 +
>  .../event-member-invalid-dict.json            |  2 +
>  .../qapi-schema/event-member-invalid-dict.out |  0
>  tests/qapi-schema/event-nest-struct.json      |  2 +-
>  .../flat-union-inline-invalid-dict.err        |  1 +
>  .../flat-union-inline-invalid-dict.exit       |  1 +
>  .../flat-union-inline-invalid-dict.json       | 11 +++
>  .../flat-union-inline-invalid-dict.out        |  0
>  tests/qapi-schema/flat-union-inline.json      |  2 +-
>  .../nested-struct-data-invalid-dict.err       |  1 +
>  .../nested-struct-data-invalid-dict.exit      |  1 +
>  .../nested-struct-data-invalid-dict.json      |  3 +
>  .../nested-struct-data-invalid-dict.out       |  0
>  tests/qapi-schema/nested-struct-data.json     |  2 +-
>  tests/qapi-schema/qapi-schema-test.json       | 10 +--
>  .../struct-member-invalid-dict.err            |  1 +
>  .../struct-member-invalid-dict.exit           |  1 +
>  .../struct-member-invalid-dict.json           |  3 +
>  .../struct-member-invalid-dict.out            |  0
>  .../qapi-schema/union-branch-invalid-dict.err |  1 +
>  .../union-branch-invalid-dict.exit            |  1 +
>  .../union-branch-invalid-dict.json            |  4 ++
>  .../qapi-schema/union-branch-invalid-dict.out |  0
>  30 files changed, 106 insertions(+), 26 deletions(-)
>  create mode 100644 tests/qapi-schema/alternate-invalid-dict.err
>  create mode 100644 tests/qapi-schema/alternate-invalid-dict.exit
>  create mode 100644 tests/qapi-schema/alternate-invalid-dict.json
>  create mode 100644 tests/qapi-schema/alternate-invalid-dict.out
>  create mode 100644 tests/qapi-schema/event-member-invalid-dict.err
>  create mode 100644 tests/qapi-schema/event-member-invalid-dict.exit
>  create mode 100644 tests/qapi-schema/event-member-invalid-dict.json
>  create mode 100644 tests/qapi-schema/event-member-invalid-dict.out
>  create mode 100644 tests/qapi-schema/flat-union-inline-invalid-dict.err
>  create mode 100644 tests/qapi-schema/flat-union-inline-invalid-dict.exit
>  create mode 100644 tests/qapi-schema/flat-union-inline-invalid-dict.json
>  create mode 100644 tests/qapi-schema/flat-union-inline-invalid-dict.out
>  create mode 100644 tests/qapi-schema/nested-struct-data-invalid-dict.err
>  create mode 100644 tests/qapi-schema/nested-struct-data-invalid-dict.exit
>  create mode 100644 tests/qapi-schema/nested-struct-data-invalid-dict.json
>  create mode 100644 tests/qapi-schema/nested-struct-data-invalid-dict.out
>  create mode 100644 tests/qapi-schema/struct-member-invalid-dict.err
>  create mode 100644 tests/qapi-schema/struct-member-invalid-dict.exit
>  create mode 100644 tests/qapi-schema/struct-member-invalid-dict.json
>  create mode 100644 tests/qapi-schema/struct-member-invalid-dict.out
>  create mode 100644 tests/qapi-schema/union-branch-invalid-dict.err
>  create mode 100644 tests/qapi-schema/union-branch-invalid-dict.exit
>  create mode 100644 tests/qapi-schema/union-branch-invalid-dict.json
>  create mode 100644 tests/qapi-schema/union-branch-invalid-dict.out
>
> diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
> index 95e55b3f44..4b3ba53dc7 100644
> --- a/scripts/qapi/common.py
> +++ b/scripts/qapi/common.py
> @@ -588,11 +588,13 @@ def discriminator_find_enum_define(expr):
>      if not base_members:
>          return None
>  
> -    discriminator_type = base_members.get(discriminator)
> -    if not discriminator_type:
> +    discriminator_value = base_members.get(discriminator)
> +    if not discriminator_value:
>          return None
>  
> -    return enum_types.get(discriminator_type)
> +    if isinstance(discriminator_value, dict):
> +        discriminator_value = discriminator_value['type']
> +    return enum_types.get(discriminator_value)

As in PATCH 08, this is slightly more complicated than v6 because @expr
isn't normalized.  I can't say offhand whether normalizing in the place
I suggested in my review of v6 would avoid this complication.

>  
>  
>  # Names must be letters, numbers, -, and _.  They must start with letter,
> @@ -660,6 +662,15 @@ def check_if(expr, info):
>          check_if_str(ifcond, info)
>  
>  
> +def normalize_members(expr, field):
> +    members = expr.get(field)
> +    if isinstance(members, OrderedDict):
> +        for key, arg in members.items():
> +            if isinstance(arg, dict):
> +                continue
> +            members[key] = {'type': arg}
> +
> +
>  def check_type(info, source, value, allow_array=False,
>                 allow_implicit=False, allow_optional=False,
>                 allow_metas=[]):
> @@ -704,8 +715,14 @@ def check_type(info, source, value, allow_array=False,
>                                 % (source, key))
>          # Todo: allow dictionaries to represent default values of
>          # an optional argument.
> -        check_type(info, "Member '%s' of %s" % (key, source), arg,
> -                   allow_array=True,
> +        if isinstance(arg, dict):
> +            check_known_keys(info, "member '%s' of %s" % (key, source),
> +                             arg, ['type'], [])
> +            typ = arg['type']
> +        else:
> +            typ = arg
> +        check_type(info, "Member '%s' of %s" % (key, source),
> +                   typ, allow_array=True,
>                     allow_metas=['built-in', 'union', 'alternate', 'struct',
>                                  'enum'])
>  

Slightly more complicated than v6, this time because members of the
implicit struct type aren't normalized, yet.

> @@ -776,13 +793,15 @@ def check_union(expr, info):
>          # member of the base struct.
>          check_name(info, "Discriminator of flat union '%s'" % name,
>                     discriminator)
> -        discriminator_type = base_members.get(discriminator)
> -        if not discriminator_type:
> +        discriminator_value = base_members.get(discriminator)
> +        if not discriminator_value:
>              raise QAPISemError(info,
>                                 "Discriminator '%s' is not a member of base "
>                                 "struct '%s'"
>                                 % (discriminator, base))
> -        enum_define = enum_types.get(discriminator_type)
> +        if isinstance(discriminator_value, dict):
> +            discriminator_value = discriminator_value['type']
> +        enum_define = enum_types.get(discriminator_value)
>          allow_metas = ['struct']
>          # Do not allow string discriminator
>          if not enum_define:
> @@ -795,10 +814,16 @@ def check_union(expr, info):
>          raise QAPISemError(info, "Union '%s' cannot have empty 'data'" % name)
>      for (key, value) in members.items():
>          check_name(info, "Member of union '%s'" % name, key)
> +        if isinstance(value, dict):
> +            check_known_keys(info, "member '%s' of union '%s'" % (key, name),
> +                             value, ['type'], [])
> +            typ = value['type']
> +        else:
> +            typ = value

Likewise.

>  
>          # Each value must name a known type
>          check_type(info, "Member '%s' of union '%s'" % (key, name),
> -                   value, allow_array=not base, allow_metas=allow_metas)
> +                   typ, allow_array=not base, allow_metas=allow_metas)
>  
>          # If the discriminator names an enum type, then all members
>          # of 'data' must also be members of the enum type.
> @@ -822,18 +847,24 @@ def check_alternate(expr, info):
>                             "in 'data'" % name)
>      for (key, value) in members.items():
>          check_name(info, "Member of alternate '%s'" % name, key)
> +        if isinstance(value, dict):
> +            check_known_keys(info,
> +                             "member '%s' of alternate '%s'" % (key, name),
> +                             value, ['type'], [])
> +            typ = value['type']
> +        else:
> +            typ = value

Likewise.

>  
>          # Ensure alternates have no type conflicts.
> -        check_type(info, "Member '%s' of alternate '%s'" % (key, name),
> -                   value,
> +        check_type(info, "Member '%s' of alternate '%s'" % (key, name), typ,
>                     allow_metas=['built-in', 'union', 'struct', 'enum'])
> -        qtype = find_alternate_member_qtype(value)
> +        qtype = find_alternate_member_qtype(typ)
>          if not qtype:
>              raise QAPISemError(info, "Alternate '%s' member '%s' cannot use "
> -                               "type '%s'" % (name, key, value))
> +                               "type '%s'" % (name, key, typ))
>          conflicting = set([qtype])
>          if qtype == 'QTYPE_QSTRING':
> -            enum_expr = enum_types.get(value)
> +            enum_expr = enum_types.get(typ)
>              if enum_expr:
>                  for v in enum_get_names(enum_expr):
>                      if v in ['on', 'off']:
> @@ -1035,6 +1066,10 @@ def normalize_exprs(exprs):
>          expr = expr_elem['expr']
>          if 'enum' in expr:
>              normalize_enum(expr)
> +        elif 'union' in expr:
> +            normalize_members(expr, 'base')
> +        if {'union', 'alternate', 'struct', 'command', 'event'} & set(expr):
> +            normalize_members(expr, 'data')
>  
>      return exprs
>  
> @@ -1738,7 +1773,7 @@ class QAPISchema(object):
>          return QAPISchemaObjectTypeMember(name, typ, optional)
>  
>      def _make_members(self, data, info):
> -        return [self._make_member(key, value, info)
> +        return [self._make_member(key, value['type'], info)
>                  for (key, value) in data.items()]
>  
>      def _def_struct_type(self, expr, info, doc):
> @@ -1774,11 +1809,11 @@ class QAPISchema(object):
>                  name, info, doc, ifcond,
>                  'base', self._make_members(base, info))
>          if tag_name:
> -            variants = [self._make_variant(key, value)
> +            variants = [self._make_variant(key, value['type'])
>                          for (key, value) in data.items()]
>              members = []
>          else:
> -            variants = [self._make_simple_variant(key, value, info)
> +            variants = [self._make_simple_variant(key, value['type'], info)
>                          for (key, value) in data.items()]
>              typ = self._make_implicit_enum_type(name, info, ifcond,
>                                                  [v.name for v in variants])
> @@ -1794,7 +1829,7 @@ class QAPISchema(object):
>          name = expr['alternate']
>          data = expr['data']
>          ifcond = expr.get('if')
> -        variants = [self._make_variant(key, value)
> +        variants = [self._make_variant(key, value['type'])
>                      for (key, value) in data.items()]
>          tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
>          self._def_entity(
> diff --git a/tests/Makefile.include b/tests/Makefile.include
> index 3c9eea27fd..ea5d1e8787 100644
> --- a/tests/Makefile.include
> +++ b/tests/Makefile.include
> @@ -318,6 +318,7 @@ qapi-schema += alternate-conflict-string.json
>  qapi-schema += alternate-conflict-bool-string.json
>  qapi-schema += alternate-conflict-num-string.json
>  qapi-schema += alternate-empty.json
> +qapi-schema += alternate-invalid-dict.json
>  qapi-schema += alternate-nested.json
>  qapi-schema += alternate-unknown.json
>  qapi-schema += args-alternate.json
> @@ -394,6 +395,7 @@ qapi-schema += escape-too-big.json
>  qapi-schema += escape-too-short.json
>  qapi-schema += event-boxed-empty.json
>  qapi-schema += event-case.json
> +qapi-schema += event-member-invalid-dict.json
>  qapi-schema += event-nest-struct.json
>  qapi-schema += flat-union-array-branch.json
>  qapi-schema += flat-union-bad-base.json
> @@ -403,6 +405,7 @@ qapi-schema += flat-union-base-union.json
>  qapi-schema += flat-union-clash-member.json
>  qapi-schema += flat-union-empty.json
>  qapi-schema += flat-union-inline.json
> +qapi-schema += flat-union-inline-invalid-dict.json
>  qapi-schema += flat-union-int-branch.json
>  qapi-schema += flat-union-invalid-branch-key.json
>  qapi-schema += flat-union-invalid-discriminator.json
> @@ -430,6 +433,7 @@ qapi-schema += missing-comma-list.json
>  qapi-schema += missing-comma-object.json
>  qapi-schema += missing-type.json
>  qapi-schema += nested-struct-data.json
> +qapi-schema += nested-struct-data-invalid-dict.json
>  qapi-schema += non-objects.json
>  qapi-schema += oob-test.json
>  qapi-schema += allow-preconfig-test.json
> @@ -460,6 +464,7 @@ qapi-schema += returns-whitelist.json
>  qapi-schema += struct-base-clash-deep.json
>  qapi-schema += struct-base-clash.json
>  qapi-schema += struct-data-invalid.json
> +qapi-schema += struct-member-invalid-dict.json
>  qapi-schema += struct-member-invalid.json
>  qapi-schema += trailing-comma-list.json
>  qapi-schema += trailing-comma-object.json
> @@ -471,6 +476,7 @@ qapi-schema += unicode-str.json
>  qapi-schema += union-base-empty.json
>  qapi-schema += union-base-no-discriminator.json
>  qapi-schema += union-branch-case.json
> +qapi-schema += union-branch-invalid-dict.json
>  qapi-schema += union-clash-branches.json
>  qapi-schema += union-empty.json
>  qapi-schema += union-invalid-base.json
> diff --git a/tests/qapi-schema/alternate-invalid-dict.err b/tests/qapi-schema/alternate-invalid-dict.err
> new file mode 100644
> index 0000000000..631d46628e
> --- /dev/null
> +++ b/tests/qapi-schema/alternate-invalid-dict.err
> @@ -0,0 +1 @@
> +tests/qapi-schema/alternate-invalid-dict.json:2: Key 'type' is missing from member 'two' of alternate 'Alt'
> diff --git a/tests/qapi-schema/alternate-invalid-dict.exit b/tests/qapi-schema/alternate-invalid-dict.exit
> new file mode 100644
> index 0000000000..d00491fd7e
> --- /dev/null
> +++ b/tests/qapi-schema/alternate-invalid-dict.exit
> @@ -0,0 +1 @@
> +1
> diff --git a/tests/qapi-schema/alternate-invalid-dict.json b/tests/qapi-schema/alternate-invalid-dict.json
> new file mode 100644
> index 0000000000..8e0b2ac287
> --- /dev/null
> +++ b/tests/qapi-schema/alternate-invalid-dict.json
> @@ -0,0 +1,4 @@
> +# exploded member form must have a 'type'
> +{ 'alternate': 'Alt',
> +  'data': { 'one': 'str',
> +            'two': { 'if': 'foo' } } }
> diff --git a/tests/qapi-schema/alternate-invalid-dict.out b/tests/qapi-schema/alternate-invalid-dict.out
> new file mode 100644
> index 0000000000..e69de29bb2
> diff --git a/tests/qapi-schema/event-member-invalid-dict.err b/tests/qapi-schema/event-member-invalid-dict.err
> new file mode 100644
> index 0000000000..1a57fa29b0
> --- /dev/null
> +++ b/tests/qapi-schema/event-member-invalid-dict.err
> @@ -0,0 +1 @@
> +tests/qapi-schema/event-member-invalid-dict.json:1: Key 'type' is missing from member 'a' of 'data' for event 'EVENT_A'
> diff --git a/tests/qapi-schema/event-member-invalid-dict.exit b/tests/qapi-schema/event-member-invalid-dict.exit
> new file mode 100644
> index 0000000000..d00491fd7e
> --- /dev/null
> +++ b/tests/qapi-schema/event-member-invalid-dict.exit
> @@ -0,0 +1 @@
> +1
> diff --git a/tests/qapi-schema/event-member-invalid-dict.json b/tests/qapi-schema/event-member-invalid-dict.json
> new file mode 100644
> index 0000000000..ee6f3ecb6f
> --- /dev/null
> +++ b/tests/qapi-schema/event-member-invalid-dict.json
> @@ -0,0 +1,2 @@
> +{ 'event': 'EVENT_A',
> +  'data': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' } }
> diff --git a/tests/qapi-schema/event-member-invalid-dict.out b/tests/qapi-schema/event-member-invalid-dict.out
> new file mode 100644
> index 0000000000..e69de29bb2
> diff --git a/tests/qapi-schema/event-nest-struct.json b/tests/qapi-schema/event-nest-struct.json
> index ee6f3ecb6f..355ddaeff1 100644
> --- a/tests/qapi-schema/event-nest-struct.json
> +++ b/tests/qapi-schema/event-nest-struct.json
> @@ -1,2 +1,2 @@
>  { 'event': 'EVENT_A',
> -  'data': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' } }
> +  'data': { 'a' : { 'type' : { 'integer': 'int' } }, 'b' : 'str' } }
> diff --git a/tests/qapi-schema/flat-union-inline-invalid-dict.err b/tests/qapi-schema/flat-union-inline-invalid-dict.err
> new file mode 100644
> index 0000000000..9c4c45b7f0
> --- /dev/null
> +++ b/tests/qapi-schema/flat-union-inline-invalid-dict.err
> @@ -0,0 +1 @@
> +tests/qapi-schema/flat-union-inline-invalid-dict.json:7: Key 'type' is missing from member 'value1' of union 'TestUnion'
> diff --git a/tests/qapi-schema/flat-union-inline-invalid-dict.exit b/tests/qapi-schema/flat-union-inline-invalid-dict.exit
> new file mode 100644
> index 0000000000..d00491fd7e
> --- /dev/null
> +++ b/tests/qapi-schema/flat-union-inline-invalid-dict.exit
> @@ -0,0 +1 @@
> +1
> diff --git a/tests/qapi-schema/flat-union-inline-invalid-dict.json b/tests/qapi-schema/flat-union-inline-invalid-dict.json
> new file mode 100644
> index 0000000000..62c7cda617
> --- /dev/null
> +++ b/tests/qapi-schema/flat-union-inline-invalid-dict.json
> @@ -0,0 +1,11 @@
> +# we require branches to be a struct name
> +# TODO: should we allow anonymous inline branch types?
> +{ 'enum': 'TestEnum',
> +  'data': [ 'value1', 'value2' ] }
> +{ 'struct': 'Base',
> +  'data': { 'enum1': 'TestEnum', 'kind': 'str' } }
> +{ 'union': 'TestUnion',
> +  'base': 'Base',
> +  'discriminator': 'enum1',
> +  'data': { 'value1': { 'string': 'str' },
> +            'value2': { 'integer': 'int' } } }
> diff --git a/tests/qapi-schema/flat-union-inline-invalid-dict.out b/tests/qapi-schema/flat-union-inline-invalid-dict.out
> new file mode 100644
> index 0000000000..e69de29bb2
> diff --git a/tests/qapi-schema/flat-union-inline.json b/tests/qapi-schema/flat-union-inline.json
> index 62c7cda617..a9b3ce3f0d 100644
> --- a/tests/qapi-schema/flat-union-inline.json
> +++ b/tests/qapi-schema/flat-union-inline.json
> @@ -7,5 +7,5 @@
>  { 'union': 'TestUnion',
>    'base': 'Base',
>    'discriminator': 'enum1',
> -  'data': { 'value1': { 'string': 'str' },
> +  'data': { 'value1': { 'type': {} },
>              'value2': { 'integer': 'int' } } }
> diff --git a/tests/qapi-schema/nested-struct-data-invalid-dict.err b/tests/qapi-schema/nested-struct-data-invalid-dict.err
> new file mode 100644
> index 0000000000..5bd364e8d9
> --- /dev/null
> +++ b/tests/qapi-schema/nested-struct-data-invalid-dict.err
> @@ -0,0 +1 @@
> +tests/qapi-schema/nested-struct-data-invalid-dict.json:2: Key 'type' is missing from member 'a' of 'data' for command 'foo'
> diff --git a/tests/qapi-schema/nested-struct-data-invalid-dict.exit b/tests/qapi-schema/nested-struct-data-invalid-dict.exit
> new file mode 100644
> index 0000000000..d00491fd7e
> --- /dev/null
> +++ b/tests/qapi-schema/nested-struct-data-invalid-dict.exit
> @@ -0,0 +1 @@
> +1
> diff --git a/tests/qapi-schema/nested-struct-data-invalid-dict.json b/tests/qapi-schema/nested-struct-data-invalid-dict.json
> new file mode 100644
> index 0000000000..efbe773ded
> --- /dev/null
> +++ b/tests/qapi-schema/nested-struct-data-invalid-dict.json
> @@ -0,0 +1,3 @@
> +# inline subtypes collide with our desired future use of defaults
> +{ 'command': 'foo',
> +  'data': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' } }
> diff --git a/tests/qapi-schema/nested-struct-data-invalid-dict.out b/tests/qapi-schema/nested-struct-data-invalid-dict.out
> new file mode 100644
> index 0000000000..e69de29bb2
> diff --git a/tests/qapi-schema/nested-struct-data.json b/tests/qapi-schema/nested-struct-data.json
> index efbe773ded..5b8a40cca3 100644
> --- a/tests/qapi-schema/nested-struct-data.json
> +++ b/tests/qapi-schema/nested-struct-data.json
> @@ -1,3 +1,3 @@
>  # inline subtypes collide with our desired future use of defaults
>  { 'command': 'foo',
> -  'data': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' } }
> +  'data': { 'a' : { 'type': {} }, 'b' : 'str' } }
> diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
> index 22d9044a89..94570154c9 100644
> --- a/tests/qapi-schema/qapi-schema-test.json
> +++ b/tests/qapi-schema/qapi-schema-test.json
> @@ -11,7 +11,7 @@
>          'guest-sync' ] } }
>  
>  { 'struct': 'TestStruct',
> -  'data': { 'integer': 'int', 'boolean': 'bool', 'string': 'str' } }
> +  'data': { 'integer': {'type': 'int'}, 'boolean': 'bool', 'string': 'str' } }
>  
>  # for testing enums
>  { 'struct': 'NestedEnumsOne',
> @@ -77,7 +77,7 @@
>  { 'union': 'UserDefFlatUnion',
>    'base': 'UserDefUnionBase',   # intentional forward reference
>    'discriminator': 'enum1',
> -  'data': { 'value1' : 'UserDefA',
> +  'data': { 'value1' : {'type': 'UserDefA'},
>              'value2' : 'UserDefB',
>              'value3' : 'UserDefB'
>              # 'value4' defaults to empty
> @@ -98,7 +98,7 @@
>  { 'struct': 'WrapAlternate',
>    'data': { 'alt': 'UserDefAlternate' } }
>  { 'alternate': 'UserDefAlternate',
> -  'data': { 'udfu': 'UserDefFlatUnion', 'e': 'EnumOne', 'i': 'int',
> +  'data': { 'udfu': {'type': 'UserDefFlatUnion'}, 'e': 'EnumOne', 'i': 'int',
>              'n': 'null' } }
>  
>  { 'struct': 'UserDefC',
> @@ -134,7 +134,7 @@
>  { 'command': 'user_def_cmd', 'data': {} }
>  { 'command': 'user_def_cmd1', 'data': {'ud1a': 'UserDefOne'} }
>  { 'command': 'user_def_cmd2',
> -  'data': {'ud1a': 'UserDefOne', '*ud1b': 'UserDefOne'},
> +  'data': {'ud1a': {'type': 'UserDefOne'}, '*ud1b': 'UserDefOne'},
>    'returns': 'UserDefTwo' }
>  
>  { 'command': 'cmd-success-response', 'data': {}, 'success-response': false }
> @@ -166,7 +166,7 @@
>  
>  # testing event
>  { 'struct': 'EventStructOne',
> -  'data': { 'struct1': 'UserDefOne', 'string': 'str', '*enum2': 'EnumOne' } }
> +  'data': { 'struct1': {'type': 'UserDefOne'}, 'string': 'str', '*enum2': 'EnumOne' } }
>  
>  { 'event': 'EVENT_A' }
>  { 'event': 'EVENT_B',
> diff --git a/tests/qapi-schema/struct-member-invalid-dict.err b/tests/qapi-schema/struct-member-invalid-dict.err
> new file mode 100644
> index 0000000000..6a765bc668
> --- /dev/null
> +++ b/tests/qapi-schema/struct-member-invalid-dict.err
> @@ -0,0 +1 @@
> +tests/qapi-schema/struct-member-invalid-dict.json:2: Key 'type' is missing from member '*a' of 'data' for struct 'foo'
> diff --git a/tests/qapi-schema/struct-member-invalid-dict.exit b/tests/qapi-schema/struct-member-invalid-dict.exit
> new file mode 100644
> index 0000000000..d00491fd7e
> --- /dev/null
> +++ b/tests/qapi-schema/struct-member-invalid-dict.exit
> @@ -0,0 +1 @@
> +1
> diff --git a/tests/qapi-schema/struct-member-invalid-dict.json b/tests/qapi-schema/struct-member-invalid-dict.json
> new file mode 100644
> index 0000000000..9fe0d455a9
> --- /dev/null
> +++ b/tests/qapi-schema/struct-member-invalid-dict.json
> @@ -0,0 +1,3 @@
> +# Long form of member must have a value member 'type'
> +{ 'struct': 'foo',
> +  'data': { '*a': { 'case': 'foo' } } }
> diff --git a/tests/qapi-schema/struct-member-invalid-dict.out b/tests/qapi-schema/struct-member-invalid-dict.out
> new file mode 100644
> index 0000000000..e69de29bb2
> diff --git a/tests/qapi-schema/union-branch-invalid-dict.err b/tests/qapi-schema/union-branch-invalid-dict.err
> new file mode 100644
> index 0000000000..89f9b36791
> --- /dev/null
> +++ b/tests/qapi-schema/union-branch-invalid-dict.err
> @@ -0,0 +1 @@
> +tests/qapi-schema/union-branch-invalid-dict.json:2: Key 'type' is missing from member 'integer' of union 'UnionInvalidBranch'
> diff --git a/tests/qapi-schema/union-branch-invalid-dict.exit b/tests/qapi-schema/union-branch-invalid-dict.exit
> new file mode 100644
> index 0000000000..d00491fd7e
> --- /dev/null
> +++ b/tests/qapi-schema/union-branch-invalid-dict.exit
> @@ -0,0 +1 @@
> +1
> diff --git a/tests/qapi-schema/union-branch-invalid-dict.json b/tests/qapi-schema/union-branch-invalid-dict.json
> new file mode 100644
> index 0000000000..9778598dbd
> --- /dev/null
> +++ b/tests/qapi-schema/union-branch-invalid-dict.json
> @@ -0,0 +1,4 @@
> +# Long form of member must have a value member 'type'
> +{ 'union': 'UnionInvalidBranch',
> +  'data': { 'integer': { 'if': 'foo'},
> +            's8': 'int8' } }
> diff --git a/tests/qapi-schema/union-branch-invalid-dict.out b/tests/qapi-schema/union-branch-invalid-dict.out
> new file mode 100644
> index 0000000000..e69de29bb2

Looks good, but I'd like to investigate whether my suggested placement
of the normalization would simplify things before I give my R-by.

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

* Re: [Qemu-devel] [PATCH for-4.0 v7 15/27] qapi: add an error in case a discriminator is conditional
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 15/27] qapi: add an error in case a discriminator is conditional Marc-André Lureau
@ 2018-12-10 17:31   ` Markus Armbruster
  2018-12-11 15:23     ` Markus Armbruster
  0 siblings, 1 reply; 69+ messages in thread
From: Markus Armbruster @ 2018-12-10 17:31 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, Michael Roth

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

> Making a discriminator conditional doesn't make much sense.

Good point (so easy to overlook!), but why first add the 'if' feature
broken that way in PATCH 13, then fix it up in PATCH 15?

>                                                             Instead,
> the union could be made conditional.

What do you mean by that?

> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  scripts/qapi/common.py                          |  8 ++++++++
>  tests/Makefile.include                          |  1 +
>  .../flat-union-invalid-if-discriminator.err     |  1 +
>  .../flat-union-invalid-if-discriminator.exit    |  1 +
>  .../flat-union-invalid-if-discriminator.json    | 17 +++++++++++++++++
>  .../flat-union-invalid-if-discriminator.out     |  0
>  6 files changed, 28 insertions(+)
>  create mode 100644 tests/qapi-schema/flat-union-invalid-if-discriminator.err
>  create mode 100644 tests/qapi-schema/flat-union-invalid-if-discriminator.exit
>  create mode 100644 tests/qapi-schema/flat-union-invalid-if-discriminator.json
>  create mode 100644 tests/qapi-schema/flat-union-invalid-if-discriminator.out
>
> diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
> index aec51acb07..f79b3fad54 100644
> --- a/scripts/qapi/common.py
> +++ b/scripts/qapi/common.py
> @@ -578,6 +578,7 @@ def find_alternate_member_qtype(qapi_type):
>  # Return the discriminator enum define if discriminator is specified as an
>  # enum type, otherwise return None.
>  def discriminator_find_enum_define(expr):
> +    name = expr['union']
>      base = expr.get('base')
>      discriminator = expr.get('discriminator')
>  
> @@ -594,6 +595,7 @@ def discriminator_find_enum_define(expr):
>  
>      if isinstance(discriminator_value, dict):
>          discriminator_value = discriminator_value['type']
> +
>      return enum_types.get(discriminator_value)
>  
>  

These two hunks are leftovers, please drop.

> @@ -800,7 +802,12 @@ def check_union(expr, info):
>                                 "struct '%s'"
>                                 % (discriminator, base))
>          if isinstance(discriminator_value, dict):
> +            if discriminator_value.get('if'):
> +                raise QAPISemError(info, 'The discriminator %s.%s for union %s '
> +                                   'must not be conditional' %
> +                                   (base, discriminator, name))
>              discriminator_value = discriminator_value['type']
> +
>          enum_define = enum_types.get(discriminator_value)
>          allow_metas = ['struct']
>          # Do not allow string discriminator
> @@ -1023,6 +1030,7 @@ def check_exprs(exprs):
>  
>          if 'include' in expr:
>              continue
> +        info = expr_elem['info']
>          if 'union' in expr and not discriminator_find_enum_define(expr):
>              name = '%sKind' % expr['union']
>          elif 'alternate' in expr:
> diff --git a/tests/Makefile.include b/tests/Makefile.include
> index ea5d1e8787..3f5a1d0c30 100644
> --- a/tests/Makefile.include
> +++ b/tests/Makefile.include
> @@ -409,6 +409,7 @@ qapi-schema += flat-union-inline-invalid-dict.json
>  qapi-schema += flat-union-int-branch.json
>  qapi-schema += flat-union-invalid-branch-key.json
>  qapi-schema += flat-union-invalid-discriminator.json
> +qapi-schema += flat-union-invalid-if-discriminator.json
>  qapi-schema += flat-union-no-base.json
>  qapi-schema += flat-union-optional-discriminator.json
>  qapi-schema += flat-union-string-discriminator.json
> diff --git a/tests/qapi-schema/flat-union-invalid-if-discriminator.err b/tests/qapi-schema/flat-union-invalid-if-discriminator.err
> new file mode 100644
> index 0000000000..0c94c9860d
> --- /dev/null
> +++ b/tests/qapi-schema/flat-union-invalid-if-discriminator.err
> @@ -0,0 +1 @@
> +tests/qapi-schema/flat-union-invalid-if-discriminator.json:13: The discriminator TestBase.enum1 for union TestUnion must not be conditional
> diff --git a/tests/qapi-schema/flat-union-invalid-if-discriminator.exit b/tests/qapi-schema/flat-union-invalid-if-discriminator.exit
> new file mode 100644
> index 0000000000..d00491fd7e
> --- /dev/null
> +++ b/tests/qapi-schema/flat-union-invalid-if-discriminator.exit
> @@ -0,0 +1 @@
> +1
> diff --git a/tests/qapi-schema/flat-union-invalid-if-discriminator.json b/tests/qapi-schema/flat-union-invalid-if-discriminator.json
> new file mode 100644
> index 0000000000..618ec36396
> --- /dev/null
> +++ b/tests/qapi-schema/flat-union-invalid-if-discriminator.json
> @@ -0,0 +1,17 @@
> +{ 'enum': 'TestEnum',
> +  'data': [ 'value1', 'value2' ] }
> +
> +{ 'struct': 'TestBase',
> +  'data': { 'enum1': { 'type': 'TestEnum', 'if': 'FOO' } } }
> +
> +{ 'struct': 'TestTypeA',
> +  'data': { 'string': 'str' } }
> +
> +{ 'struct': 'TestTypeB',
> +  'data': { 'integer': 'int' } }
> +
> +{ 'union': 'TestUnion',
> +  'base': 'TestBase',
> +  'discriminator': 'enum1',
> +  'data': { 'value1': 'TestTypeA',
> +            'value2': 'TestTypeB' } }
> diff --git a/tests/qapi-schema/flat-union-invalid-if-discriminator.out b/tests/qapi-schema/flat-union-invalid-if-discriminator.out
> new file mode 100644
> index 0000000000..e69de29bb2

Patch looks good otherwise.

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

* Re: [Qemu-devel] [PATCH for-4.0 v7 16/27] qapi: add 'if' to union members
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 16/27] qapi: add 'if' to union members Marc-André Lureau
@ 2018-12-10 17:33   ` Markus Armbruster
  0 siblings, 0 replies; 69+ messages in thread
From: Markus Armbruster @ 2018-12-10 17:33 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, Michael Roth

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

> Add 'if' key to union members:
>
> { 'union': 'TestIfUnion', 'data':
>     'mem': { 'type': 'str', 'if': 'COND'} }
>
> The generated code remains unconditional for now. Later patches
> generate the conditionals.
>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

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

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

* Re: [Qemu-devel] [PATCH for-4.0 v7 17/27] qapi: simplify make_enum_members()
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 17/27] qapi: simplify make_enum_members() Marc-André Lureau
@ 2018-12-11 12:05   ` Markus Armbruster
  2018-12-11 12:08     ` Marc-André Lureau
  0 siblings, 1 reply; 69+ messages in thread
From: Markus Armbruster @ 2018-12-11 12:05 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, Michael Roth

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

> The function only receives the dictionary form of enum expressions
> now, so we can make it shorter.
>
> Suggested-by: Markus Armbruster <armbru@redhat.com>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  scripts/qapi/common.py | 11 +----------
>  1 file changed, 1 insertion(+), 10 deletions(-)
>
> diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
> index fd622313cb..44020779dd 100644
> --- a/scripts/qapi/common.py
> +++ b/scripts/qapi/common.py
> @@ -1714,16 +1714,7 @@ class QAPISchema(object):
>                                              qtype_values, 'QTYPE'))
>  
>      def _make_enum_members(self, values):
> -        enum = []
> -        for v in values:
> -            if isinstance(v, dict):
> -                name = v['name']
> -                ifcond = v.get('if')
> -            else:
> -                name = v
> -                ifcond = None
> -            enum.append(QAPISchemaMember(name, ifcond))
> -        return enum
> +        return [QAPISchemaMember(v['name'], v.get('if')) for v in values]
>  
>      def _make_implicit_enum_type(self, name, info, ifcond, values):
>          # See also QAPISchemaObjectTypeMember._pretty_owner()

We may want to squash this one into PATCH 08 as mentioned in that
patch's review.

In case we don't:
Reviewed-by: Markus Armbruster <armbru@redhat.com>

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

* Re: [Qemu-devel] [PATCH for-4.0 v7 17/27] qapi: simplify make_enum_members()
  2018-12-11 12:05   ` Markus Armbruster
@ 2018-12-11 12:08     ` Marc-André Lureau
  0 siblings, 0 replies; 69+ messages in thread
From: Marc-André Lureau @ 2018-12-11 12:08 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: QEMU, Michael Roth

Hi

On Tue, Dec 11, 2018 at 4:06 PM Markus Armbruster <armbru@redhat.com> wrote:
>
> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
>
> > The function only receives the dictionary form of enum expressions
> > now, so we can make it shorter.
> >
> > Suggested-by: Markus Armbruster <armbru@redhat.com>
> > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> > ---
> >  scripts/qapi/common.py | 11 +----------
> >  1 file changed, 1 insertion(+), 10 deletions(-)
> >
> > diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
> > index fd622313cb..44020779dd 100644
> > --- a/scripts/qapi/common.py
> > +++ b/scripts/qapi/common.py
> > @@ -1714,16 +1714,7 @@ class QAPISchema(object):
> >                                              qtype_values, 'QTYPE'))
> >
> >      def _make_enum_members(self, values):
> > -        enum = []
> > -        for v in values:
> > -            if isinstance(v, dict):
> > -                name = v['name']
> > -                ifcond = v.get('if')
> > -            else:
> > -                name = v
> > -                ifcond = None
> > -            enum.append(QAPISchemaMember(name, ifcond))
> > -        return enum
> > +        return [QAPISchemaMember(v['name'], v.get('if')) for v in values]
> >
> >      def _make_implicit_enum_type(self, name, info, ifcond, values):
> >          # See also QAPISchemaObjectTypeMember._pretty_owner()
>
> We may want to squash this one into PATCH 08 as mentioned in that
> patch's review.
>
> In case we don't:
> Reviewed-by: Markus Armbruster <armbru@redhat.com>
>

Feel free to squash on commit, thanks

-- 
Marc-André Lureau

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

* Re: [Qemu-devel] [PATCH for-4.0 v7 18/27] tests/qapi: add command with condition on union argument
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 18/27] tests/qapi: add command with condition on union argument Marc-André Lureau
@ 2018-12-11 12:09   ` Markus Armbruster
  2018-12-11 12:13     ` Marc-André Lureau
  0 siblings, 1 reply; 69+ messages in thread
From: Markus Armbruster @ 2018-12-11 12:09 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, Michael Roth

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

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

Let's say "Forgotten in commit 967c885108f."

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

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

* Re: [Qemu-devel] [PATCH for-4.0 v7 13/27] qapi: add a dictionary form for TYPE
  2018-12-10 17:24   ` Markus Armbruster
@ 2018-12-11 12:11     ` Marc-André Lureau
  2018-12-11 15:35       ` Markus Armbruster
  2018-12-11 15:18     ` Markus Armbruster
  1 sibling, 1 reply; 69+ messages in thread
From: Marc-André Lureau @ 2018-12-11 12:11 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: QEMU, Michael Roth

Hi

On Mon, Dec 10, 2018 at 9:26 PM Markus Armbruster <armbru@redhat.com> wrote:
>
> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
>
> > Wherever a struct/union/alternate/command/event member with NAME: TYPE
> > form is accepted, desugar it to a NAME: { 'type': TYPE } form.
> >
> > This will allow to add new member details, such as 'if' in the
> > following patch to introduce conditionals, or 'default' for default
> > values etc.
> >
> > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> > ---
> >  scripts/qapi/common.py                        | 71 ++++++++++++++-----
> >  tests/Makefile.include                        |  6 ++
> >  tests/qapi-schema/alternate-invalid-dict.err  |  1 +
> >  tests/qapi-schema/alternate-invalid-dict.exit |  1 +
> >  tests/qapi-schema/alternate-invalid-dict.json |  4 ++
> >  tests/qapi-schema/alternate-invalid-dict.out  |  0
> >  .../qapi-schema/event-member-invalid-dict.err |  1 +
> >  .../event-member-invalid-dict.exit            |  1 +
> >  .../event-member-invalid-dict.json            |  2 +
> >  .../qapi-schema/event-member-invalid-dict.out |  0
> >  tests/qapi-schema/event-nest-struct.json      |  2 +-
> >  .../flat-union-inline-invalid-dict.err        |  1 +
> >  .../flat-union-inline-invalid-dict.exit       |  1 +
> >  .../flat-union-inline-invalid-dict.json       | 11 +++
> >  .../flat-union-inline-invalid-dict.out        |  0
> >  tests/qapi-schema/flat-union-inline.json      |  2 +-
> >  .../nested-struct-data-invalid-dict.err       |  1 +
> >  .../nested-struct-data-invalid-dict.exit      |  1 +
> >  .../nested-struct-data-invalid-dict.json      |  3 +
> >  .../nested-struct-data-invalid-dict.out       |  0
> >  tests/qapi-schema/nested-struct-data.json     |  2 +-
> >  tests/qapi-schema/qapi-schema-test.json       | 10 +--
> >  .../struct-member-invalid-dict.err            |  1 +
> >  .../struct-member-invalid-dict.exit           |  1 +
> >  .../struct-member-invalid-dict.json           |  3 +
> >  .../struct-member-invalid-dict.out            |  0
> >  .../qapi-schema/union-branch-invalid-dict.err |  1 +
> >  .../union-branch-invalid-dict.exit            |  1 +
> >  .../union-branch-invalid-dict.json            |  4 ++
> >  .../qapi-schema/union-branch-invalid-dict.out |  0
> >  30 files changed, 106 insertions(+), 26 deletions(-)
> >  create mode 100644 tests/qapi-schema/alternate-invalid-dict.err
> >  create mode 100644 tests/qapi-schema/alternate-invalid-dict.exit
> >  create mode 100644 tests/qapi-schema/alternate-invalid-dict.json
> >  create mode 100644 tests/qapi-schema/alternate-invalid-dict.out
> >  create mode 100644 tests/qapi-schema/event-member-invalid-dict.err
> >  create mode 100644 tests/qapi-schema/event-member-invalid-dict.exit
> >  create mode 100644 tests/qapi-schema/event-member-invalid-dict.json
> >  create mode 100644 tests/qapi-schema/event-member-invalid-dict.out
> >  create mode 100644 tests/qapi-schema/flat-union-inline-invalid-dict.err
> >  create mode 100644 tests/qapi-schema/flat-union-inline-invalid-dict.exit
> >  create mode 100644 tests/qapi-schema/flat-union-inline-invalid-dict.json
> >  create mode 100644 tests/qapi-schema/flat-union-inline-invalid-dict.out
> >  create mode 100644 tests/qapi-schema/nested-struct-data-invalid-dict.err
> >  create mode 100644 tests/qapi-schema/nested-struct-data-invalid-dict.exit
> >  create mode 100644 tests/qapi-schema/nested-struct-data-invalid-dict.json
> >  create mode 100644 tests/qapi-schema/nested-struct-data-invalid-dict.out
> >  create mode 100644 tests/qapi-schema/struct-member-invalid-dict.err
> >  create mode 100644 tests/qapi-schema/struct-member-invalid-dict.exit
> >  create mode 100644 tests/qapi-schema/struct-member-invalid-dict.json
> >  create mode 100644 tests/qapi-schema/struct-member-invalid-dict.out
> >  create mode 100644 tests/qapi-schema/union-branch-invalid-dict.err
> >  create mode 100644 tests/qapi-schema/union-branch-invalid-dict.exit
> >  create mode 100644 tests/qapi-schema/union-branch-invalid-dict.json
> >  create mode 100644 tests/qapi-schema/union-branch-invalid-dict.out
> >
> > diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
> > index 95e55b3f44..4b3ba53dc7 100644
> > --- a/scripts/qapi/common.py
> > +++ b/scripts/qapi/common.py
> > @@ -588,11 +588,13 @@ def discriminator_find_enum_define(expr):
> >      if not base_members:
> >          return None
> >
> > -    discriminator_type = base_members.get(discriminator)
> > -    if not discriminator_type:
> > +    discriminator_value = base_members.get(discriminator)
> > +    if not discriminator_value:
> >          return None
> >
> > -    return enum_types.get(discriminator_type)
> > +    if isinstance(discriminator_value, dict):
> > +        discriminator_value = discriminator_value['type']
> > +    return enum_types.get(discriminator_value)
>
> As in PATCH 08, this is slightly more complicated than v6 because @expr
> isn't normalized.  I can't say offhand whether normalizing in the place
> I suggested in my review of v6 would avoid this complication.
>
> >
> >
> >  # Names must be letters, numbers, -, and _.  They must start with letter,
> > @@ -660,6 +662,15 @@ def check_if(expr, info):
> >          check_if_str(ifcond, info)
> >
> >
> > +def normalize_members(expr, field):
> > +    members = expr.get(field)
> > +    if isinstance(members, OrderedDict):
> > +        for key, arg in members.items():
> > +            if isinstance(arg, dict):
> > +                continue
> > +            members[key] = {'type': arg}
> > +
> > +
> >  def check_type(info, source, value, allow_array=False,
> >                 allow_implicit=False, allow_optional=False,
> >                 allow_metas=[]):
> > @@ -704,8 +715,14 @@ def check_type(info, source, value, allow_array=False,
> >                                 % (source, key))
> >          # Todo: allow dictionaries to represent default values of
> >          # an optional argument.
> > -        check_type(info, "Member '%s' of %s" % (key, source), arg,
> > -                   allow_array=True,
> > +        if isinstance(arg, dict):
> > +            check_known_keys(info, "member '%s' of %s" % (key, source),
> > +                             arg, ['type'], [])
> > +            typ = arg['type']
> > +        else:
> > +            typ = arg
> > +        check_type(info, "Member '%s' of %s" % (key, source),
> > +                   typ, allow_array=True,
> >                     allow_metas=['built-in', 'union', 'alternate', 'struct',
> >                                  'enum'])
> >
>
> Slightly more complicated than v6, this time because members of the
> implicit struct type aren't normalized, yet.
>
> > @@ -776,13 +793,15 @@ def check_union(expr, info):
> >          # member of the base struct.
> >          check_name(info, "Discriminator of flat union '%s'" % name,
> >                     discriminator)
> > -        discriminator_type = base_members.get(discriminator)
> > -        if not discriminator_type:
> > +        discriminator_value = base_members.get(discriminator)
> > +        if not discriminator_value:
> >              raise QAPISemError(info,
> >                                 "Discriminator '%s' is not a member of base "
> >                                 "struct '%s'"
> >                                 % (discriminator, base))
> > -        enum_define = enum_types.get(discriminator_type)
> > +        if isinstance(discriminator_value, dict):
> > +            discriminator_value = discriminator_value['type']
> > +        enum_define = enum_types.get(discriminator_value)
> >          allow_metas = ['struct']
> >          # Do not allow string discriminator
> >          if not enum_define:
> > @@ -795,10 +814,16 @@ def check_union(expr, info):
> >          raise QAPISemError(info, "Union '%s' cannot have empty 'data'" % name)
> >      for (key, value) in members.items():
> >          check_name(info, "Member of union '%s'" % name, key)
> > +        if isinstance(value, dict):
> > +            check_known_keys(info, "member '%s' of union '%s'" % (key, name),
> > +                             value, ['type'], [])
> > +            typ = value['type']
> > +        else:
> > +            typ = value
>
> Likewise.
>
> >
> >          # Each value must name a known type
> >          check_type(info, "Member '%s' of union '%s'" % (key, name),
> > -                   value, allow_array=not base, allow_metas=allow_metas)
> > +                   typ, allow_array=not base, allow_metas=allow_metas)
> >
> >          # If the discriminator names an enum type, then all members
> >          # of 'data' must also be members of the enum type.
> > @@ -822,18 +847,24 @@ def check_alternate(expr, info):
> >                             "in 'data'" % name)
> >      for (key, value) in members.items():
> >          check_name(info, "Member of alternate '%s'" % name, key)
> > +        if isinstance(value, dict):
> > +            check_known_keys(info,
> > +                             "member '%s' of alternate '%s'" % (key, name),
> > +                             value, ['type'], [])
> > +            typ = value['type']
> > +        else:
> > +            typ = value
>
> Likewise.
>
> >
> >          # Ensure alternates have no type conflicts.
> > -        check_type(info, "Member '%s' of alternate '%s'" % (key, name),
> > -                   value,
> > +        check_type(info, "Member '%s' of alternate '%s'" % (key, name), typ,
> >                     allow_metas=['built-in', 'union', 'struct', 'enum'])
> > -        qtype = find_alternate_member_qtype(value)
> > +        qtype = find_alternate_member_qtype(typ)
> >          if not qtype:
> >              raise QAPISemError(info, "Alternate '%s' member '%s' cannot use "
> > -                               "type '%s'" % (name, key, value))
> > +                               "type '%s'" % (name, key, typ))
> >          conflicting = set([qtype])
> >          if qtype == 'QTYPE_QSTRING':
> > -            enum_expr = enum_types.get(value)
> > +            enum_expr = enum_types.get(typ)
> >              if enum_expr:
> >                  for v in enum_get_names(enum_expr):
> >                      if v in ['on', 'off']:
> > @@ -1035,6 +1066,10 @@ def normalize_exprs(exprs):
> >          expr = expr_elem['expr']
> >          if 'enum' in expr:
> >              normalize_enum(expr)
> > +        elif 'union' in expr:
> > +            normalize_members(expr, 'base')
> > +        if {'union', 'alternate', 'struct', 'command', 'event'} & set(expr):
> > +            normalize_members(expr, 'data')
> >
> >      return exprs
> >
> > @@ -1738,7 +1773,7 @@ class QAPISchema(object):
> >          return QAPISchemaObjectTypeMember(name, typ, optional)
> >
> >      def _make_members(self, data, info):
> > -        return [self._make_member(key, value, info)
> > +        return [self._make_member(key, value['type'], info)
> >                  for (key, value) in data.items()]
> >
> >      def _def_struct_type(self, expr, info, doc):
> > @@ -1774,11 +1809,11 @@ class QAPISchema(object):
> >                  name, info, doc, ifcond,
> >                  'base', self._make_members(base, info))
> >          if tag_name:
> > -            variants = [self._make_variant(key, value)
> > +            variants = [self._make_variant(key, value['type'])
> >                          for (key, value) in data.items()]
> >              members = []
> >          else:
> > -            variants = [self._make_simple_variant(key, value, info)
> > +            variants = [self._make_simple_variant(key, value['type'], info)
> >                          for (key, value) in data.items()]
> >              typ = self._make_implicit_enum_type(name, info, ifcond,
> >                                                  [v.name for v in variants])
> > @@ -1794,7 +1829,7 @@ class QAPISchema(object):
> >          name = expr['alternate']
> >          data = expr['data']
> >          ifcond = expr.get('if')
> > -        variants = [self._make_variant(key, value)
> > +        variants = [self._make_variant(key, value['type'])
> >                      for (key, value) in data.items()]
> >          tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
> >          self._def_entity(
> > diff --git a/tests/Makefile.include b/tests/Makefile.include
> > index 3c9eea27fd..ea5d1e8787 100644
> > --- a/tests/Makefile.include
> > +++ b/tests/Makefile.include
> > @@ -318,6 +318,7 @@ qapi-schema += alternate-conflict-string.json
> >  qapi-schema += alternate-conflict-bool-string.json
> >  qapi-schema += alternate-conflict-num-string.json
> >  qapi-schema += alternate-empty.json
> > +qapi-schema += alternate-invalid-dict.json
> >  qapi-schema += alternate-nested.json
> >  qapi-schema += alternate-unknown.json
> >  qapi-schema += args-alternate.json
> > @@ -394,6 +395,7 @@ qapi-schema += escape-too-big.json
> >  qapi-schema += escape-too-short.json
> >  qapi-schema += event-boxed-empty.json
> >  qapi-schema += event-case.json
> > +qapi-schema += event-member-invalid-dict.json
> >  qapi-schema += event-nest-struct.json
> >  qapi-schema += flat-union-array-branch.json
> >  qapi-schema += flat-union-bad-base.json
> > @@ -403,6 +405,7 @@ qapi-schema += flat-union-base-union.json
> >  qapi-schema += flat-union-clash-member.json
> >  qapi-schema += flat-union-empty.json
> >  qapi-schema += flat-union-inline.json
> > +qapi-schema += flat-union-inline-invalid-dict.json
> >  qapi-schema += flat-union-int-branch.json
> >  qapi-schema += flat-union-invalid-branch-key.json
> >  qapi-schema += flat-union-invalid-discriminator.json
> > @@ -430,6 +433,7 @@ qapi-schema += missing-comma-list.json
> >  qapi-schema += missing-comma-object.json
> >  qapi-schema += missing-type.json
> >  qapi-schema += nested-struct-data.json
> > +qapi-schema += nested-struct-data-invalid-dict.json
> >  qapi-schema += non-objects.json
> >  qapi-schema += oob-test.json
> >  qapi-schema += allow-preconfig-test.json
> > @@ -460,6 +464,7 @@ qapi-schema += returns-whitelist.json
> >  qapi-schema += struct-base-clash-deep.json
> >  qapi-schema += struct-base-clash.json
> >  qapi-schema += struct-data-invalid.json
> > +qapi-schema += struct-member-invalid-dict.json
> >  qapi-schema += struct-member-invalid.json
> >  qapi-schema += trailing-comma-list.json
> >  qapi-schema += trailing-comma-object.json
> > @@ -471,6 +476,7 @@ qapi-schema += unicode-str.json
> >  qapi-schema += union-base-empty.json
> >  qapi-schema += union-base-no-discriminator.json
> >  qapi-schema += union-branch-case.json
> > +qapi-schema += union-branch-invalid-dict.json
> >  qapi-schema += union-clash-branches.json
> >  qapi-schema += union-empty.json
> >  qapi-schema += union-invalid-base.json
> > diff --git a/tests/qapi-schema/alternate-invalid-dict.err b/tests/qapi-schema/alternate-invalid-dict.err
> > new file mode 100644
> > index 0000000000..631d46628e
> > --- /dev/null
> > +++ b/tests/qapi-schema/alternate-invalid-dict.err
> > @@ -0,0 +1 @@
> > +tests/qapi-schema/alternate-invalid-dict.json:2: Key 'type' is missing from member 'two' of alternate 'Alt'
> > diff --git a/tests/qapi-schema/alternate-invalid-dict.exit b/tests/qapi-schema/alternate-invalid-dict.exit
> > new file mode 100644
> > index 0000000000..d00491fd7e
> > --- /dev/null
> > +++ b/tests/qapi-schema/alternate-invalid-dict.exit
> > @@ -0,0 +1 @@
> > +1
> > diff --git a/tests/qapi-schema/alternate-invalid-dict.json b/tests/qapi-schema/alternate-invalid-dict.json
> > new file mode 100644
> > index 0000000000..8e0b2ac287
> > --- /dev/null
> > +++ b/tests/qapi-schema/alternate-invalid-dict.json
> > @@ -0,0 +1,4 @@
> > +# exploded member form must have a 'type'
> > +{ 'alternate': 'Alt',
> > +  'data': { 'one': 'str',
> > +            'two': { 'if': 'foo' } } }
> > diff --git a/tests/qapi-schema/alternate-invalid-dict.out b/tests/qapi-schema/alternate-invalid-dict.out
> > new file mode 100644
> > index 0000000000..e69de29bb2
> > diff --git a/tests/qapi-schema/event-member-invalid-dict.err b/tests/qapi-schema/event-member-invalid-dict.err
> > new file mode 100644
> > index 0000000000..1a57fa29b0
> > --- /dev/null
> > +++ b/tests/qapi-schema/event-member-invalid-dict.err
> > @@ -0,0 +1 @@
> > +tests/qapi-schema/event-member-invalid-dict.json:1: Key 'type' is missing from member 'a' of 'data' for event 'EVENT_A'
> > diff --git a/tests/qapi-schema/event-member-invalid-dict.exit b/tests/qapi-schema/event-member-invalid-dict.exit
> > new file mode 100644
> > index 0000000000..d00491fd7e
> > --- /dev/null
> > +++ b/tests/qapi-schema/event-member-invalid-dict.exit
> > @@ -0,0 +1 @@
> > +1
> > diff --git a/tests/qapi-schema/event-member-invalid-dict.json b/tests/qapi-schema/event-member-invalid-dict.json
> > new file mode 100644
> > index 0000000000..ee6f3ecb6f
> > --- /dev/null
> > +++ b/tests/qapi-schema/event-member-invalid-dict.json
> > @@ -0,0 +1,2 @@
> > +{ 'event': 'EVENT_A',
> > +  'data': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' } }
> > diff --git a/tests/qapi-schema/event-member-invalid-dict.out b/tests/qapi-schema/event-member-invalid-dict.out
> > new file mode 100644
> > index 0000000000..e69de29bb2
> > diff --git a/tests/qapi-schema/event-nest-struct.json b/tests/qapi-schema/event-nest-struct.json
> > index ee6f3ecb6f..355ddaeff1 100644
> > --- a/tests/qapi-schema/event-nest-struct.json
> > +++ b/tests/qapi-schema/event-nest-struct.json
> > @@ -1,2 +1,2 @@
> >  { 'event': 'EVENT_A',
> > -  'data': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' } }
> > +  'data': { 'a' : { 'type' : { 'integer': 'int' } }, 'b' : 'str' } }
> > diff --git a/tests/qapi-schema/flat-union-inline-invalid-dict.err b/tests/qapi-schema/flat-union-inline-invalid-dict.err
> > new file mode 100644
> > index 0000000000..9c4c45b7f0
> > --- /dev/null
> > +++ b/tests/qapi-schema/flat-union-inline-invalid-dict.err
> > @@ -0,0 +1 @@
> > +tests/qapi-schema/flat-union-inline-invalid-dict.json:7: Key 'type' is missing from member 'value1' of union 'TestUnion'
> > diff --git a/tests/qapi-schema/flat-union-inline-invalid-dict.exit b/tests/qapi-schema/flat-union-inline-invalid-dict.exit
> > new file mode 100644
> > index 0000000000..d00491fd7e
> > --- /dev/null
> > +++ b/tests/qapi-schema/flat-union-inline-invalid-dict.exit
> > @@ -0,0 +1 @@
> > +1
> > diff --git a/tests/qapi-schema/flat-union-inline-invalid-dict.json b/tests/qapi-schema/flat-union-inline-invalid-dict.json
> > new file mode 100644
> > index 0000000000..62c7cda617
> > --- /dev/null
> > +++ b/tests/qapi-schema/flat-union-inline-invalid-dict.json
> > @@ -0,0 +1,11 @@
> > +# we require branches to be a struct name
> > +# TODO: should we allow anonymous inline branch types?
> > +{ 'enum': 'TestEnum',
> > +  'data': [ 'value1', 'value2' ] }
> > +{ 'struct': 'Base',
> > +  'data': { 'enum1': 'TestEnum', 'kind': 'str' } }
> > +{ 'union': 'TestUnion',
> > +  'base': 'Base',
> > +  'discriminator': 'enum1',
> > +  'data': { 'value1': { 'string': 'str' },
> > +            'value2': { 'integer': 'int' } } }
> > diff --git a/tests/qapi-schema/flat-union-inline-invalid-dict.out b/tests/qapi-schema/flat-union-inline-invalid-dict.out
> > new file mode 100644
> > index 0000000000..e69de29bb2
> > diff --git a/tests/qapi-schema/flat-union-inline.json b/tests/qapi-schema/flat-union-inline.json
> > index 62c7cda617..a9b3ce3f0d 100644
> > --- a/tests/qapi-schema/flat-union-inline.json
> > +++ b/tests/qapi-schema/flat-union-inline.json
> > @@ -7,5 +7,5 @@
> >  { 'union': 'TestUnion',
> >    'base': 'Base',
> >    'discriminator': 'enum1',
> > -  'data': { 'value1': { 'string': 'str' },
> > +  'data': { 'value1': { 'type': {} },
> >              'value2': { 'integer': 'int' } } }
> > diff --git a/tests/qapi-schema/nested-struct-data-invalid-dict.err b/tests/qapi-schema/nested-struct-data-invalid-dict.err
> > new file mode 100644
> > index 0000000000..5bd364e8d9
> > --- /dev/null
> > +++ b/tests/qapi-schema/nested-struct-data-invalid-dict.err
> > @@ -0,0 +1 @@
> > +tests/qapi-schema/nested-struct-data-invalid-dict.json:2: Key 'type' is missing from member 'a' of 'data' for command 'foo'
> > diff --git a/tests/qapi-schema/nested-struct-data-invalid-dict.exit b/tests/qapi-schema/nested-struct-data-invalid-dict.exit
> > new file mode 100644
> > index 0000000000..d00491fd7e
> > --- /dev/null
> > +++ b/tests/qapi-schema/nested-struct-data-invalid-dict.exit
> > @@ -0,0 +1 @@
> > +1
> > diff --git a/tests/qapi-schema/nested-struct-data-invalid-dict.json b/tests/qapi-schema/nested-struct-data-invalid-dict.json
> > new file mode 100644
> > index 0000000000..efbe773ded
> > --- /dev/null
> > +++ b/tests/qapi-schema/nested-struct-data-invalid-dict.json
> > @@ -0,0 +1,3 @@
> > +# inline subtypes collide with our desired future use of defaults
> > +{ 'command': 'foo',
> > +  'data': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' } }
> > diff --git a/tests/qapi-schema/nested-struct-data-invalid-dict.out b/tests/qapi-schema/nested-struct-data-invalid-dict.out
> > new file mode 100644
> > index 0000000000..e69de29bb2
> > diff --git a/tests/qapi-schema/nested-struct-data.json b/tests/qapi-schema/nested-struct-data.json
> > index efbe773ded..5b8a40cca3 100644
> > --- a/tests/qapi-schema/nested-struct-data.json
> > +++ b/tests/qapi-schema/nested-struct-data.json
> > @@ -1,3 +1,3 @@
> >  # inline subtypes collide with our desired future use of defaults
> >  { 'command': 'foo',
> > -  'data': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' } }
> > +  'data': { 'a' : { 'type': {} }, 'b' : 'str' } }
> > diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
> > index 22d9044a89..94570154c9 100644
> > --- a/tests/qapi-schema/qapi-schema-test.json
> > +++ b/tests/qapi-schema/qapi-schema-test.json
> > @@ -11,7 +11,7 @@
> >          'guest-sync' ] } }
> >
> >  { 'struct': 'TestStruct',
> > -  'data': { 'integer': 'int', 'boolean': 'bool', 'string': 'str' } }
> > +  'data': { 'integer': {'type': 'int'}, 'boolean': 'bool', 'string': 'str' } }
> >
> >  # for testing enums
> >  { 'struct': 'NestedEnumsOne',
> > @@ -77,7 +77,7 @@
> >  { 'union': 'UserDefFlatUnion',
> >    'base': 'UserDefUnionBase',   # intentional forward reference
> >    'discriminator': 'enum1',
> > -  'data': { 'value1' : 'UserDefA',
> > +  'data': { 'value1' : {'type': 'UserDefA'},
> >              'value2' : 'UserDefB',
> >              'value3' : 'UserDefB'
> >              # 'value4' defaults to empty
> > @@ -98,7 +98,7 @@
> >  { 'struct': 'WrapAlternate',
> >    'data': { 'alt': 'UserDefAlternate' } }
> >  { 'alternate': 'UserDefAlternate',
> > -  'data': { 'udfu': 'UserDefFlatUnion', 'e': 'EnumOne', 'i': 'int',
> > +  'data': { 'udfu': {'type': 'UserDefFlatUnion'}, 'e': 'EnumOne', 'i': 'int',
> >              'n': 'null' } }
> >
> >  { 'struct': 'UserDefC',
> > @@ -134,7 +134,7 @@
> >  { 'command': 'user_def_cmd', 'data': {} }
> >  { 'command': 'user_def_cmd1', 'data': {'ud1a': 'UserDefOne'} }
> >  { 'command': 'user_def_cmd2',
> > -  'data': {'ud1a': 'UserDefOne', '*ud1b': 'UserDefOne'},
> > +  'data': {'ud1a': {'type': 'UserDefOne'}, '*ud1b': 'UserDefOne'},
> >    'returns': 'UserDefTwo' }
> >
> >  { 'command': 'cmd-success-response', 'data': {}, 'success-response': false }
> > @@ -166,7 +166,7 @@
> >
> >  # testing event
> >  { 'struct': 'EventStructOne',
> > -  'data': { 'struct1': 'UserDefOne', 'string': 'str', '*enum2': 'EnumOne' } }
> > +  'data': { 'struct1': {'type': 'UserDefOne'}, 'string': 'str', '*enum2': 'EnumOne' } }
> >
> >  { 'event': 'EVENT_A' }
> >  { 'event': 'EVENT_B',
> > diff --git a/tests/qapi-schema/struct-member-invalid-dict.err b/tests/qapi-schema/struct-member-invalid-dict.err
> > new file mode 100644
> > index 0000000000..6a765bc668
> > --- /dev/null
> > +++ b/tests/qapi-schema/struct-member-invalid-dict.err
> > @@ -0,0 +1 @@
> > +tests/qapi-schema/struct-member-invalid-dict.json:2: Key 'type' is missing from member '*a' of 'data' for struct 'foo'
> > diff --git a/tests/qapi-schema/struct-member-invalid-dict.exit b/tests/qapi-schema/struct-member-invalid-dict.exit
> > new file mode 100644
> > index 0000000000..d00491fd7e
> > --- /dev/null
> > +++ b/tests/qapi-schema/struct-member-invalid-dict.exit
> > @@ -0,0 +1 @@
> > +1
> > diff --git a/tests/qapi-schema/struct-member-invalid-dict.json b/tests/qapi-schema/struct-member-invalid-dict.json
> > new file mode 100644
> > index 0000000000..9fe0d455a9
> > --- /dev/null
> > +++ b/tests/qapi-schema/struct-member-invalid-dict.json
> > @@ -0,0 +1,3 @@
> > +# Long form of member must have a value member 'type'
> > +{ 'struct': 'foo',
> > +  'data': { '*a': { 'case': 'foo' } } }
> > diff --git a/tests/qapi-schema/struct-member-invalid-dict.out b/tests/qapi-schema/struct-member-invalid-dict.out
> > new file mode 100644
> > index 0000000000..e69de29bb2
> > diff --git a/tests/qapi-schema/union-branch-invalid-dict.err b/tests/qapi-schema/union-branch-invalid-dict.err
> > new file mode 100644
> > index 0000000000..89f9b36791
> > --- /dev/null
> > +++ b/tests/qapi-schema/union-branch-invalid-dict.err
> > @@ -0,0 +1 @@
> > +tests/qapi-schema/union-branch-invalid-dict.json:2: Key 'type' is missing from member 'integer' of union 'UnionInvalidBranch'
> > diff --git a/tests/qapi-schema/union-branch-invalid-dict.exit b/tests/qapi-schema/union-branch-invalid-dict.exit
> > new file mode 100644
> > index 0000000000..d00491fd7e
> > --- /dev/null
> > +++ b/tests/qapi-schema/union-branch-invalid-dict.exit
> > @@ -0,0 +1 @@
> > +1
> > diff --git a/tests/qapi-schema/union-branch-invalid-dict.json b/tests/qapi-schema/union-branch-invalid-dict.json
> > new file mode 100644
> > index 0000000000..9778598dbd
> > --- /dev/null
> > +++ b/tests/qapi-schema/union-branch-invalid-dict.json
> > @@ -0,0 +1,4 @@
> > +# Long form of member must have a value member 'type'
> > +{ 'union': 'UnionInvalidBranch',
> > +  'data': { 'integer': { 'if': 'foo'},
> > +            's8': 'int8' } }
> > diff --git a/tests/qapi-schema/union-branch-invalid-dict.out b/tests/qapi-schema/union-branch-invalid-dict.out
> > new file mode 100644
> > index 0000000000..e69de29bb2
>
> Looks good, but I'd like to investigate whether my suggested placement
> of the normalization would simplify things before I give my R-by.
>

I am not sure I follow, you suggest to have normalization before
check? Then how can we produce an error that reflects the input
accurately? Should we carry additional information (original form etc)
on the source during normalization?

-- 
Marc-André Lureau

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

* Re: [Qemu-devel] [PATCH for-4.0 v7 18/27] tests/qapi: add command with condition on union argument
  2018-12-11 12:09   ` Markus Armbruster
@ 2018-12-11 12:13     ` Marc-André Lureau
  0 siblings, 0 replies; 69+ messages in thread
From: Marc-André Lureau @ 2018-12-11 12:13 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: QEMU, Michael Roth

On Tue, Dec 11, 2018 at 4:09 PM Markus Armbruster <armbru@redhat.com> wrote:
>
> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
>
> > For completeness.
> >
> > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>
> Let's say "Forgotten in commit 967c885108f."
>
> Reviewed-by: Markus Armbruster <armbru@redhat.com>

ack, thanks


-- 
Marc-André Lureau

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

* Re: [Qemu-devel] [PATCH for-4.0 v7 20/27] tests/qapi: add command with condition on alternate argument
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 20/27] tests/qapi: add command with condition on alternate argument Marc-André Lureau
@ 2018-12-11 12:20   ` Markus Armbruster
  0 siblings, 0 replies; 69+ messages in thread
From: Markus Armbruster @ 2018-12-11 12:20 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, armbru, Michael Roth

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

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

Let's say "Forgotten in commit 967c885108f."

We could squash PATCH 18 and this one together.  In case we don't:
Reviewed-by: Markus Armbruster <armbru@redhat.com>

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

* Re: [Qemu-devel] [PATCH for-4.0 v7 19/27] qapi: add 'if' to alternate members
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 19/27] qapi: add 'if' to alternate members Marc-André Lureau
@ 2018-12-11 12:21   ` Markus Armbruster
  0 siblings, 0 replies; 69+ messages in thread
From: Markus Armbruster @ 2018-12-11 12:21 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, Michael Roth

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

> Add 'if' key to alternate members:
>
> { 'alternate': 'TestIfAlternate', 'data':
>   { 'alt': { 'type': 'TestStruct', 'if': 'COND' } } }
>
> Generated code is not changed by this patch but with "qapi: add #if
> conditions to generated code".
>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

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

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

* Re: [Qemu-devel] [PATCH for-4.0 v7 21/27] qapi: add #if conditions to generated code members
  2018-12-08 11:16 ` [Qemu-devel] [PATCH for-4.0 v7 21/27] qapi: add #if conditions to generated code members Marc-André Lureau
@ 2018-12-11 13:17   ` Markus Armbruster
  0 siblings, 0 replies; 69+ messages in thread
From: Markus Armbruster @ 2018-12-11 13:17 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, Michael Roth

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

> Wrap generated enum/struct members and code with #if/#endif, using the
> .ifcond members added in the previous patches.
>
> Some types generate both enum and struct members for example, so a
> step-by-step is unnecessarily complicated to deal with (it would
> easily generate invalid intermediary code).
>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

We've since agreed to tweak the commit message:

    qapi: Add #if conditions to generated code members

    Wrap generated enum and struct members and their supporting code with
    #if/#endif, using the .ifcond members added in the previous patches.

    We do enum and struct in a single patch because union tag enum and the
    associated variants tie them together, and dealing with that to split
    the patch doesn't seem worthwhile.

> ---
>  scripts/qapi/common.py     |  4 ++++
>  scripts/qapi/introspect.py | 13 +++++++++----
>  scripts/qapi/types.py      |  4 ++++
>  scripts/qapi/visit.py      |  6 ++++++
>  4 files changed, 23 insertions(+), 4 deletions(-)
>
> diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
> index a70d6dec3b..3a29812ef2 100644
> --- a/scripts/qapi/common.py
> +++ b/scripts/qapi/common.py
> @@ -2108,11 +2108,13 @@ const QEnumLookup %(c_name)s_lookup = {
>  ''',
>                  c_name=c_name(name))
>      for m in members:
> +        ret += gen_if(m.ifcond)
>          index = c_enum_const(name, m.name, prefix)
>          ret += mcgen('''
>          [%(index)s] = "%(name)s",
>  ''',
>                       index=index, name=m.name)
> +        ret += gen_endif(m.ifcond)
>  
>      ret += mcgen('''
>      },
> @@ -2134,10 +2136,12 @@ typedef enum %(c_name)s {
>                  c_name=c_name(name))
>  
>      for m in enum_members:
> +        ret += gen_if(m.ifcond)
>          ret += mcgen('''
>      %(c_enum)s,
>  ''',
>                       c_enum=c_enum_const(name, m.name, prefix))
> +        ret += gen_endif(m.ifcond)
>  
>      ret += mcgen('''
>  } %(c_name)s;
> diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py
> index 417625d54b..77087f629b 100644
> --- a/scripts/qapi/introspect.py
> +++ b/scripts/qapi/introspect.py
> @@ -162,6 +162,8 @@ const QLitObject %(c_name)s = %(c_string)s;
>          ret = {'name': member.name, 'type': self._use_type(member.type)}
>          if member.optional:
>              ret['default'] = None
> +        if member.ifcond:
> +            ret = (ret, {'if': member.ifcond})
>          return ret
>  
>      def _gen_variants(self, tag_name, variants):
> @@ -169,14 +171,16 @@ const QLitObject %(c_name)s = %(c_string)s;
>                  'variants': [self._gen_variant(v) for v in variants]}
>  
>      def _gen_variant(self, variant):
> -        return {'case': variant.name, 'type': self._use_type(variant.type)}
> +        return ({'case': variant.name, 'type': self._use_type(variant.type)},
> +                {'if': variant.ifcond})
>  
>      def visit_builtin_type(self, name, info, json_type):
>          self._gen_qlit(name, 'builtin', {'json-type': json_type}, [])
>  
>      def visit_enum_type(self, name, info, ifcond, members, prefix):
>          self._gen_qlit(name, 'enum',
> -                       {'values': [m.name for m in members]}, ifcond)
> +                       {'values': [(m.name, {'if': m.ifcond}) for m in members]},
> +                       ifcond)
>  
>      def visit_array_type(self, name, info, ifcond, element_type):
>          element = self._use_type(element_type)
> @@ -192,8 +196,9 @@ const QLitObject %(c_name)s = %(c_string)s;
>  
>      def visit_alternate_type(self, name, info, ifcond, variants):
>          self._gen_qlit(name, 'alternate',
> -                       {'members': [{'type': self._use_type(m.type)}
> -                                    for m in variants.variants]}, ifcond)
> +                       {'members': [
> +                           ({'type': self._use_type(m.type)}, {'if': m.ifcond})

Long line.  I'll feed the series to pycodestyle and pylint.

> +                           for m in variants.variants]}, ifcond)
>  
>      def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
>                        success_response, boxed, allow_oob, allow_preconfig):
> diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py
> index 0404710bbd..e2ee9f3b72 100644
> --- a/scripts/qapi/types.py
> +++ b/scripts/qapi/types.py
> @@ -43,6 +43,7 @@ struct %(c_name)s {
>  def gen_struct_members(members):
>      ret = ''
>      for memb in members:
> +        ret += gen_if(memb.ifcond)
>          if memb.optional:
>              ret += mcgen('''
>      bool has_%(c_name)s;
> @@ -52,6 +53,7 @@ def gen_struct_members(members):
>      %(c_type)s %(c_name)s;
>  ''',
>                       c_type=memb.type.c_type(), c_name=c_name(memb.name))
> +        ret += gen_endif(memb.ifcond)
>      return ret
>  
>  
> @@ -131,11 +133,13 @@ def gen_variants(variants):
>      for var in variants.variants:
>          if var.type.name == 'q_empty':
>              continue
> +        ret += gen_if(var.ifcond)
>          ret += mcgen('''
>          %(c_type)s %(c_name)s;
>  ''',
>                       c_type=var.type.c_unboxed_type(),
>                       c_name=c_name(var.name))
> +        ret += gen_endif(var.ifcond)
>  
>      ret += mcgen('''
>      } u;
> diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py
> index 24f85a2e85..82eab72b21 100644
> --- a/scripts/qapi/visit.py
> +++ b/scripts/qapi/visit.py
> @@ -54,6 +54,7 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
>                       c_type=base.c_name())
>  
>      for memb in members:
> +        ret += gen_if(memb.ifcond)
>          if memb.optional:
>              ret += mcgen('''
>      if (visit_optional(v, "%(name)s", &obj->has_%(c_name)s)) {
> @@ -73,6 +74,7 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
>              ret += mcgen('''
>      }
>  ''')
> +        ret += gen_endif(memb.ifcond)
>  
>      if variants:
>          ret += mcgen('''
> @@ -84,6 +86,7 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
>              case_str = c_enum_const(variants.tag_member.type.name,
>                                      var.name,
>                                      variants.tag_member.type.prefix)
> +            ret += gen_if(var.ifcond)
>              if var.type.name == 'q_empty':
>                  # valid variant and nothing to do
>                  ret += mcgen('''
> @@ -100,6 +103,7 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
>                               case=case_str,
>                               c_type=var.type.c_name(), c_name=c_name(var.name))
>  
> +            ret += gen_endif(var.ifcond)
>          ret += mcgen('''
>      default:
>          abort();
> @@ -190,6 +194,7 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
>                  c_name=c_name(name))
>  
>      for var in variants.variants:
> +        ret += gen_if(var.ifcond)
>          ret += mcgen('''
>      case %(case)s:
>  ''',
> @@ -217,6 +222,7 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
>          ret += mcgen('''
>          break;
>  ''')
> +        ret += gen_endif(var.ifcond)
>  
>      ret += mcgen('''
>      case QTYPE_NONE:

With the tweaked commit message:
Reviewed-by: Markus Armbruster <armbru@redhat.com>

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

* Re: [Qemu-devel] [PATCH for-4.0 v7 13/27] qapi: add a dictionary form for TYPE
  2018-12-10 17:24   ` Markus Armbruster
  2018-12-11 12:11     ` Marc-André Lureau
@ 2018-12-11 15:18     ` Markus Armbruster
  1 sibling, 0 replies; 69+ messages in thread
From: Markus Armbruster @ 2018-12-11 15:18 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, Michael Roth

New pylint gripes:

scripts/qapi/common.py:674:0: R0912: Too many branches (13/12) (too-many-branches)
scripts/qapi/common.py:764:0: R0912: Too many branches (13/12) (too-many-branches)
scripts/qapi/common.py:838:0: R0912: Too many branches (13/12) (too-many-branches)

That's check_type(), check_union(), check_alternate().  Let's ignore
these for now.

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

* Re: [Qemu-devel] [PATCH for-4.0 v7 15/27] qapi: add an error in case a discriminator is conditional
  2018-12-10 17:31   ` Markus Armbruster
@ 2018-12-11 15:23     ` Markus Armbruster
  0 siblings, 0 replies; 69+ messages in thread
From: Markus Armbruster @ 2018-12-11 15:23 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, Michael Roth

Two more nits...

Markus Armbruster <armbru@redhat.com> writes:

> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
>
>> Making a discriminator conditional doesn't make much sense.
>
> Good point (so easy to overlook!), but why first add the 'if' feature
> broken that way in PATCH 13, then fix it up in PATCH 15?
>
>>                                                             Instead,
>> the union could be made conditional.
>
> What do you mean by that?
>
>> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>> ---
>>  scripts/qapi/common.py                          |  8 ++++++++
>>  tests/Makefile.include                          |  1 +
>>  .../flat-union-invalid-if-discriminator.err     |  1 +
>>  .../flat-union-invalid-if-discriminator.exit    |  1 +
>>  .../flat-union-invalid-if-discriminator.json    | 17 +++++++++++++++++
>>  .../flat-union-invalid-if-discriminator.out     |  0
>>  6 files changed, 28 insertions(+)
>>  create mode 100644 tests/qapi-schema/flat-union-invalid-if-discriminator.err
>>  create mode 100644 tests/qapi-schema/flat-union-invalid-if-discriminator.exit
>>  create mode 100644 tests/qapi-schema/flat-union-invalid-if-discriminator.json
>>  create mode 100644 tests/qapi-schema/flat-union-invalid-if-discriminator.out
>>
>> diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
>> index aec51acb07..f79b3fad54 100644
>> --- a/scripts/qapi/common.py
>> +++ b/scripts/qapi/common.py
>> @@ -578,6 +578,7 @@ def find_alternate_member_qtype(qapi_type):
>>  # Return the discriminator enum define if discriminator is specified as an
>>  # enum type, otherwise return None.
>>  def discriminator_find_enum_define(expr):
>> +    name = expr['union']

Dead assignment.

>>      base = expr.get('base')
>>      discriminator = expr.get('discriminator')
>>  
>> @@ -594,6 +595,7 @@ def discriminator_find_enum_define(expr):
>>  
>>      if isinstance(discriminator_value, dict):
>>          discriminator_value = discriminator_value['type']
>> +
>>      return enum_types.get(discriminator_value)
>>  
>>  
>
> These two hunks are leftovers, please drop.
>
>> @@ -800,7 +802,12 @@ def check_union(expr, info):
>>                                 "struct '%s'"
>>                                 % (discriminator, base))
>>          if isinstance(discriminator_value, dict):
>> +            if discriminator_value.get('if'):
>> +                raise QAPISemError(info, 'The discriminator %s.%s for union %s '

pycodestyle complains

    scripts/qapi/common.py:806:80: E501 line too long (80 > 79 characters)

>> +                                   'must not be conditional' %
>> +                                   (base, discriminator, name))
>>              discriminator_value = discriminator_value['type']
>> +
>>          enum_define = enum_types.get(discriminator_value)
>>          allow_metas = ['struct']
>>          # Do not allow string discriminator
>> @@ -1023,6 +1030,7 @@ def check_exprs(exprs):
>>  
>>          if 'include' in expr:
>>              continue
>> +        info = expr_elem['info']
>>          if 'union' in expr and not discriminator_find_enum_define(expr):
>>              name = '%sKind' % expr['union']
>>          elif 'alternate' in expr:
>> diff --git a/tests/Makefile.include b/tests/Makefile.include
>> index ea5d1e8787..3f5a1d0c30 100644
>> --- a/tests/Makefile.include
>> +++ b/tests/Makefile.include
>> @@ -409,6 +409,7 @@ qapi-schema += flat-union-inline-invalid-dict.json
>>  qapi-schema += flat-union-int-branch.json
>>  qapi-schema += flat-union-invalid-branch-key.json
>>  qapi-schema += flat-union-invalid-discriminator.json
>> +qapi-schema += flat-union-invalid-if-discriminator.json
>>  qapi-schema += flat-union-no-base.json
>>  qapi-schema += flat-union-optional-discriminator.json
>>  qapi-schema += flat-union-string-discriminator.json
>> diff --git a/tests/qapi-schema/flat-union-invalid-if-discriminator.err b/tests/qapi-schema/flat-union-invalid-if-discriminator.err
>> new file mode 100644
>> index 0000000000..0c94c9860d
>> --- /dev/null
>> +++ b/tests/qapi-schema/flat-union-invalid-if-discriminator.err
>> @@ -0,0 +1 @@
>> +tests/qapi-schema/flat-union-invalid-if-discriminator.json:13: The discriminator TestBase.enum1 for union TestUnion must not be conditional
>> diff --git a/tests/qapi-schema/flat-union-invalid-if-discriminator.exit b/tests/qapi-schema/flat-union-invalid-if-discriminator.exit
>> new file mode 100644
>> index 0000000000..d00491fd7e
>> --- /dev/null
>> +++ b/tests/qapi-schema/flat-union-invalid-if-discriminator.exit
>> @@ -0,0 +1 @@
>> +1
>> diff --git a/tests/qapi-schema/flat-union-invalid-if-discriminator.json b/tests/qapi-schema/flat-union-invalid-if-discriminator.json
>> new file mode 100644
>> index 0000000000..618ec36396
>> --- /dev/null
>> +++ b/tests/qapi-schema/flat-union-invalid-if-discriminator.json
>> @@ -0,0 +1,17 @@
>> +{ 'enum': 'TestEnum',
>> +  'data': [ 'value1', 'value2' ] }
>> +
>> +{ 'struct': 'TestBase',
>> +  'data': { 'enum1': { 'type': 'TestEnum', 'if': 'FOO' } } }
>> +
>> +{ 'struct': 'TestTypeA',
>> +  'data': { 'string': 'str' } }
>> +
>> +{ 'struct': 'TestTypeB',
>> +  'data': { 'integer': 'int' } }
>> +
>> +{ 'union': 'TestUnion',
>> +  'base': 'TestBase',
>> +  'discriminator': 'enum1',
>> +  'data': { 'value1': 'TestTypeA',
>> +            'value2': 'TestTypeB' } }
>> diff --git a/tests/qapi-schema/flat-union-invalid-if-discriminator.out b/tests/qapi-schema/flat-union-invalid-if-discriminator.out
>> new file mode 100644
>> index 0000000000..e69de29bb2
>
> Patch looks good otherwise.

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

* Re: [Qemu-devel] [PATCH for-4.0 v7 13/27] qapi: add a dictionary form for TYPE
  2018-12-11 12:11     ` Marc-André Lureau
@ 2018-12-11 15:35       ` Markus Armbruster
  0 siblings, 0 replies; 69+ messages in thread
From: Markus Armbruster @ 2018-12-11 15:35 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: QEMU, Michael Roth

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

> Hi
>
> On Mon, Dec 10, 2018 at 9:26 PM Markus Armbruster <armbru@redhat.com> wrote:
[...]
>> Looks good, but I'd like to investigate whether my suggested placement
>> of the normalization would simplify things before I give my R-by.
>>
>
> I am not sure I follow, you suggest to have normalization before
> check? Then how can we produce an error that reflects the input
> accurately? Should we carry additional information (original form etc)
> on the source during normalization?

Let me play with it.  If my idea works out, showing it to you will be
easier than explaining it.  If it doesn't, ditching it quietly will also
be easier than explaining it :)

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

* Re: [Qemu-devel] [PATCH for-4.0 v7 01/27] qapi: make sure osdep.h is included in type headers
  2018-12-10 13:28       ` Markus Armbruster
@ 2018-12-11 15:47         ` Marc-André Lureau
  2018-12-12  6:47           ` Markus Armbruster
  2018-12-11 16:05         ` Daniel P. Berrangé
  1 sibling, 1 reply; 69+ messages in thread
From: Marc-André Lureau @ 2018-12-11 15:47 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: QEMU, Michael Roth

Hi

On Mon, Dec 10, 2018 at 5:28 PM Markus Armbruster <armbru@redhat.com> wrote:
>
> Marc-André Lureau <marcandre.lureau@gmail.com> writes:
>
> > Hi
> >
> > On Mon, Dec 10, 2018 at 1:52 PM Markus Armbruster <armbru@redhat.com> wrote:
> >>
> >> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
> >>
> >> > Now that the schema can be configured, it is crucial that all types
> >> > are configured the same. Make sure config-host.h is included, by
> >> > checking osdep.h inclusion. The build-sys tracks the dependency and
> >> > rebuilds the types if the configuration changed.
> >> >
> >> > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> >> > ---
> >> >  scripts/qapi/types.py | 3 +++
> >> >  1 file changed, 3 insertions(+)
> >> >
> >> > diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py
> >> > index fd7808103c..3bb9cb6d47 100644
> >> > --- a/scripts/qapi/types.py
> >> > +++ b/scripts/qapi/types.py
> >> > @@ -201,6 +201,9 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor):
> >> >  ''',
> >> >                                        types=types, visit=visit))
> >> >          self._genh.preamble_add(mcgen('''
> >> > +#ifndef QEMU_OSDEP_H
> >> > +#error Please include osdep.h first!
> >> > +#endif
> >> >  #include "qapi/qapi-builtin-types.h"
> >> >  '''))
> >>
> >> I understand why you propose this patch.  The QAPI-generated headers use
> >> #ifdef CONFIG_FOO.  The configuration header "qemu/osdep.h" must be
> >> consistently included before the generated headers, or else you end up
> >> with nasty bugs, such as the same enum having different values in
> >> different .o, or the same struct having a different layout.
> >>
> >> But this applies to *all* headers that use #ifdef.  Why check it here,
> >> but not there?  What makes the QAPI-generated headers special?
> >>
> >
> > It's the discussion about #if in headers (and enums in particular)
> > that started this. We want to make sure all compilation units share
> > the same data structure/ABI. I proposed to include osdep.h in qapi
> > headers, but that was rejected.
> > The warning is a different approach. I agree it could apply to all
> > headers. Do you think I should update all headers as well?
>
> That would replace the rule 'all .c files must include "qemu/osdep.h"
> first' by 'all .h files must check "qemu/osdep.h" has been included
> already', which is not an improvement.

That would be an improvement since headers may also be impacted by osdep.h

Alternatively, why not use -include ?

>
> All we have right now to help with enforcing our osdep.h rule is
> scripts/clean-includes.  We run it periodically to fix rule breakers.
> It's manual, because we have a few .c files in the tree where the rule
> doesn't apply, and the script doesn't know about them.  Fixable, I
> guess.  Most recent run:
>
>     Subject: [PATCH] Clean up includes
>     Message-Id: <20181204172535.2799-1-armbru@redhat.com>
>     https://lists.nongnu.org/archive/html/qemu-devel/2018-12/msg00605.html
>
> The obvious improvement would be flagging rule breakers before they get
> committed.
>
> checkpatch.pl could flag patches adding .c files that don't include
> "qemu/osdep.h" first.  The "first" part might be a bit annoying to code.
> The "adding files" part already exists: checkpatch.pl warns when you add
> a file without updating MAINTAINERS.
>
> checkpatch.pl could also flag patches removing #include "qemu/osdep.h".
>
> Other projects use a syntax-check make target to check sources.  If we
> decide we like that better than checkpatch.pl for some checks, we can do
> that.



-- 
Marc-André Lureau

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

* Re: [Qemu-devel] [PATCH for-4.0 v7 22/27] qapi: add 'If:' condition to enum values documentation
  2018-12-08 11:16 ` [Qemu-devel] [PATCH for-4.0 v7 22/27] qapi: add 'If:' condition to enum values documentation Marc-André Lureau
@ 2018-12-11 16:00   ` Markus Armbruster
  2018-12-11 16:07     ` Marc-André Lureau
  0 siblings, 1 reply; 69+ messages in thread
From: Markus Armbruster @ 2018-12-11 16:00 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, Michael Roth

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

> Use a common function to generate the "If:..." line.
>
> While at it, get rid of the existing \n\n (no idea why it was
> there). Use a line-break in member description, this seems to look
> slightly better in the plaintext version.

Where exactly in the patch is this done?

> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  scripts/qapi/doc.py             | 24 +++++++++++++++---------
>  tests/qapi-schema/doc-good.json |  4 +++-
>  tests/qapi-schema/doc-good.out  |  1 +
>  tests/qapi-schema/doc-good.texi |  2 +-
>  4 files changed, 20 insertions(+), 11 deletions(-)
>
> diff --git a/scripts/qapi/doc.py b/scripts/qapi/doc.py
> index 76cb186ff9..2133ded47e 100755
> --- a/scripts/qapi/doc.py
> +++ b/scripts/qapi/doc.py
> @@ -126,19 +126,26 @@ def texi_body(doc):
>      return texi_format(doc.body.text)
>  
>  
> -def texi_enum_value(value):
> +def texi_if(ifcond, prefix='\n', suffix='\n'):
> +    """Format the #if condition"""
> +    return '%s@b{If:} @code{%s}%s' % (
> +        prefix, ', '.join(ifcond), suffix) if ifcond else ''

I like the ternary operator, but I nevertheless think this function is
easier to read as

       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' % value.name
> +    return '@item @code{%s}\n%s%s' % (
> +        value.name, desc, texi_if(value.ifcond, prefix='@*'))

Do you ignore suffix intentionally?

>  
>  
> -def texi_member(member, suffix=''):
> +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' % (
> +    return '@item @code{%s%s}%s%s\n%s' % (
>          member.name, membertype,
>          ' (optional)' if member.optional else '',
> -        suffix)
> +        suffix, desc)
>  
>  
>  def texi_members(doc, what, base, variants, member_func):
> @@ -155,7 +162,7 @@ def texi_members(doc, what, base, variants, member_func):
>              desc = 'One of ' + members_text + '\n'
>          else:
>              desc = 'Not documented\n'
> -        items += member_func(section.member) + desc
> +        items += member_func(section.member, desc)

Here, you pass a @desc argument, but no @suffix.

@member_func is either texi_enum_value() or texi_member().  Works.

>      if base:
>          items += '@item The members of @code{%s}\n' % base.doc_type()
>      if variants:
> @@ -165,7 +172,7 @@ def texi_members(doc, what, base, variants, member_func):
>              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, when)
> +                    items += member_func(m, suffix=when)

Here, you pass a @suffix argument, but no @desc.

texi_enum_value() would choke on that, but it can't occur here, because
enums have no variants.

Still, I'd prefer texi_enum_value() and texi_member() to have the exact
same signature.

>              else:
>                  items += '@item The members of @code{%s}%s\n' % (
>                      v.type.doc_type(), when)
> @@ -185,8 +192,7 @@ def texi_sections(doc, ifcond):
>              body += texi_example(section.text)
>          else:
>              body += texi_format(section.text)
> -    if ifcond:
> -        body += '\n\n@b{If:} @code{%s}' % ", ".join(ifcond)
> +    body += texi_if(ifcond, suffix='')
>      return body
>  
>  
> diff --git a/tests/qapi-schema/doc-good.json b/tests/qapi-schema/doc-good.json
> index 984cd8ed06..c7fe08c530 100644
> --- a/tests/qapi-schema/doc-good.json
> +++ b/tests/qapi-schema/doc-good.json
> @@ -55,7 +55,9 @@
>  #
>  # @two is undocumented
>  ##
> -{ 'enum': 'Enum', 'data': [ 'one', 'two' ], 'if': 'defined(IFCOND)' }
> +{ 'enum': 'Enum', 'data':
> +  [ { 'name': 'one', 'if': 'defined(IFENUM)' }, 'two' ],
> +  'if': 'defined(IFCOND)' }

IFCOND applies to the enum, and IFENUM applies to one of its values.
Awkward.  Let's rename IFENUM to IFONE.

>  
>  ##
>  # @Base:
> diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out
> index c2fc5c774a..a05535b69b 100644
> --- a/tests/qapi-schema/doc-good.out
> +++ b/tests/qapi-schema/doc-good.out
> @@ -11,6 +11,7 @@ enum QType
>  module doc-good.json
>  enum Enum
>      member one
> +        if ['defined(IFENUM)']
>      member two
>      if ['defined(IFCOND)']
>  object Base
> diff --git a/tests/qapi-schema/doc-good.texi b/tests/qapi-schema/doc-good.texi
> index e42eace474..5c0231e0e6 100644
> --- a/tests/qapi-schema/doc-good.texi
> +++ b/tests/qapi-schema/doc-good.texi
> @@ -84,12 +84,12 @@ Examples:
>  @table @asis
>  @item @code{one}
>  The @emph{one} @{and only@}
> +@*@b{If:} @code{defined(IFENUM)}

@* forces a line break.  Putting it at the end of the previous line
would be tidier, but let's ignore that for now.

>  @item @code{two}
>  Not documented
>  @end table
>  @code{two} is undocumented
>  
> -
>  @b{If:} @code{defined(IFCOND)}
>  @end deftp

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

* Re: [Qemu-devel] [PATCH for-4.0 v7 01/27] qapi: make sure osdep.h is included in type headers
  2018-12-10 13:28       ` Markus Armbruster
  2018-12-11 15:47         ` Marc-André Lureau
@ 2018-12-11 16:05         ` Daniel P. Berrangé
  2018-12-12  6:48           ` Markus Armbruster
  1 sibling, 1 reply; 69+ messages in thread
From: Daniel P. Berrangé @ 2018-12-11 16:05 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: Marc-André Lureau, QEMU, Michael Roth

On Mon, Dec 10, 2018 at 02:28:26PM +0100, Markus Armbruster wrote:
> Marc-André Lureau <marcandre.lureau@gmail.com> writes:
> 
> > Hi
> >
> > On Mon, Dec 10, 2018 at 1:52 PM Markus Armbruster <armbru@redhat.com> wrote:
> >>
> >> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
> >>
> >> > Now that the schema can be configured, it is crucial that all types
> >> > are configured the same. Make sure config-host.h is included, by
> >> > checking osdep.h inclusion. The build-sys tracks the dependency and
> >> > rebuilds the types if the configuration changed.
> >> >
> >> > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> >> > ---
> >> >  scripts/qapi/types.py | 3 +++
> >> >  1 file changed, 3 insertions(+)
> >> >
> >> > diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py
> >> > index fd7808103c..3bb9cb6d47 100644
> >> > --- a/scripts/qapi/types.py
> >> > +++ b/scripts/qapi/types.py
> >> > @@ -201,6 +201,9 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor):
> >> >  ''',
> >> >                                        types=types, visit=visit))
> >> >          self._genh.preamble_add(mcgen('''
> >> > +#ifndef QEMU_OSDEP_H
> >> > +#error Please include osdep.h first!
> >> > +#endif
> >> >  #include "qapi/qapi-builtin-types.h"
> >> >  '''))
> >>
> >> I understand why you propose this patch.  The QAPI-generated headers use
> >> #ifdef CONFIG_FOO.  The configuration header "qemu/osdep.h" must be
> >> consistently included before the generated headers, or else you end up
> >> with nasty bugs, such as the same enum having different values in
> >> different .o, or the same struct having a different layout.
> >>
> >> But this applies to *all* headers that use #ifdef.  Why check it here,
> >> but not there?  What makes the QAPI-generated headers special?
> >>
> >
> > It's the discussion about #if in headers (and enums in particular)
> > that started this. We want to make sure all compilation units share
> > the same data structure/ABI. I proposed to include osdep.h in qapi
> > headers, but that was rejected.
> > The warning is a different approach. I agree it could apply to all
> > headers. Do you think I should update all headers as well?
> 
> That would replace the rule 'all .c files must include "qemu/osdep.h"
> first' by 'all .h files must check "qemu/osdep.h" has been included
> already', which is not an improvement.
> 
> All we have right now to help with enforcing our osdep.h rule is
> scripts/clean-includes.  We run it periodically to fix rule breakers.
> It's manual, because we have a few .c files in the tree where the rule
> doesn't apply, and the script doesn't know about them.  Fixable, I
> guess.  Most recent run:
> 
>     Subject: [PATCH] Clean up includes
>     Message-Id: <20181204172535.2799-1-armbru@redhat.com>
>     https://lists.nongnu.org/archive/html/qemu-devel/2018-12/msg00605.html
> 
> The obvious improvement would be flagging rule breakers before they get
> committed.
> 
> checkpatch.pl could flag patches adding .c files that don't include
> "qemu/osdep.h" first.  The "first" part might be a bit annoying to code.

You can get this logic from GNULIBs syntax-check make rules. It uses
this perl code to check that config.h is included first:

    while (<>) {
        if (/^# *include\b/) {
            if (not m{^# *include <config\.h>}) {
                print "$ARGV\n";
                $$ret = 1;
            }
            close ARGV;
        }
    }


Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|

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

* Re: [Qemu-devel] [PATCH for-4.0 v7 25/27] qapi: break long lines at 'data' member
  2018-12-08 11:16 ` [Qemu-devel] [PATCH for-4.0 v7 25/27] qapi: break long lines at 'data' member Marc-André Lureau
@ 2018-12-11 16:07   ` Markus Armbruster
  2018-12-11 16:08     ` Marc-André Lureau
  0 siblings, 1 reply; 69+ messages in thread
From: Markus Armbruster @ 2018-12-11 16:07 UTC (permalink / raw)
  To: Marc-André Lureau
  Cc: qemu-devel, Juan Quintela, Jason Wang, Dr. David Alan Gilbert,
	Gerd Hoffmann, Paolo Bonzini, Stefan Berger

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

> Let's break the line before 'data'. While at it, improve a bit
> indentation/spacing. (I removed some alignment which are not helping
> much readability and become quickly inconsistent)
>
> Suggested-by: Markus Armbruster <armbru@redhat.com>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

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

I can cherry-pick this one into my next pull request to reduce the
likelihood of conflicts.

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

* Re: [Qemu-devel] [PATCH for-4.0 v7 22/27] qapi: add 'If:' condition to enum values documentation
  2018-12-11 16:00   ` Markus Armbruster
@ 2018-12-11 16:07     ` Marc-André Lureau
  2018-12-13 13:59       ` Markus Armbruster
  0 siblings, 1 reply; 69+ messages in thread
From: Marc-André Lureau @ 2018-12-11 16:07 UTC (permalink / raw)
  To: Armbruster, Markus; +Cc: qemu-devel, Michael Roth

Hi

On Tue, Dec 11, 2018 at 8:00 PM Markus Armbruster <armbru@redhat.com> wrote:
>
> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
>
> > Use a common function to generate the "If:..." line.
> >
> > While at it, get rid of the existing \n\n (no idea why it was
> > there). Use a line-break in member description, this seems to look
> > slightly better in the plaintext version.
>
> Where exactly in the patch is this done?

-    if ifcond:
-        body += '\n\n@b{If:} @code{%s}' % ", ".join(ifcond)
+    body += texi_if(ifcond, suffix='')

See also doc-good.texi output

>
> > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> > ---
> >  scripts/qapi/doc.py             | 24 +++++++++++++++---------
> >  tests/qapi-schema/doc-good.json |  4 +++-
> >  tests/qapi-schema/doc-good.out  |  1 +
> >  tests/qapi-schema/doc-good.texi |  2 +-
> >  4 files changed, 20 insertions(+), 11 deletions(-)
> >
> > diff --git a/scripts/qapi/doc.py b/scripts/qapi/doc.py
> > index 76cb186ff9..2133ded47e 100755
> > --- a/scripts/qapi/doc.py
> > +++ b/scripts/qapi/doc.py
> > @@ -126,19 +126,26 @@ def texi_body(doc):
> >      return texi_format(doc.body.text)
> >
> >
> > -def texi_enum_value(value):
> > +def texi_if(ifcond, prefix='\n', suffix='\n'):
> > +    """Format the #if condition"""
> > +    return '%s@b{If:} @code{%s}%s' % (
> > +        prefix, ', '.join(ifcond), suffix) if ifcond else ''
>
> I like the ternary operator, but I nevertheless think this function is
> easier to read as
>
>        if not ifcond:
>            return ''
>        return '%s@b{If:} @code{%s}%s' % (prefix, ', '.join(ifcond), suffix)
>

No difference to me, sure

> > +
> > +
> > +def texi_enum_value(value, desc, suffix=''):
> >      """Format a table of members item for an enumeration value"""
> > -    return '@item @code{%s}\n' % value.name
> > +    return '@item @code{%s}\n%s%s' % (
> > +        value.name, desc, texi_if(value.ifcond, prefix='@*'))
>
> Do you ignore suffix intentionally?

yes

>
> >
> >
> > -def texi_member(member, suffix=''):
> > +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' % (
> > +    return '@item @code{%s%s}%s%s\n%s' % (
> >          member.name, membertype,
> >          ' (optional)' if member.optional else '',
> > -        suffix)
> > +        suffix, desc)
> >
> >
> >  def texi_members(doc, what, base, variants, member_func):
> > @@ -155,7 +162,7 @@ def texi_members(doc, what, base, variants, member_func):
> >              desc = 'One of ' + members_text + '\n'
> >          else:
> >              desc = 'Not documented\n'
> > -        items += member_func(section.member) + desc
> > +        items += member_func(section.member, desc)
>
> Here, you pass a @desc argument, but no @suffix.
>
> @member_func is either texi_enum_value() or texi_member().  Works.
>
> >      if base:
> >          items += '@item The members of @code{%s}\n' % base.doc_type()
> >      if variants:
> > @@ -165,7 +172,7 @@ def texi_members(doc, what, base, variants, member_func):
> >              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, when)
> > +                    items += member_func(m, suffix=when)
>
> Here, you pass a @suffix argument, but no @desc.
>
> texi_enum_value() would choke on that, but it can't occur here, because
> enums have no variants.
>
> Still, I'd prefer texi_enum_value() and texi_member() to have the exact
> same signature.

OK (not sure what is the more pythonic way)

>
> >              else:
> >                  items += '@item The members of @code{%s}%s\n' % (
> >                      v.type.doc_type(), when)
> > @@ -185,8 +192,7 @@ def texi_sections(doc, ifcond):
> >              body += texi_example(section.text)
> >          else:
> >              body += texi_format(section.text)
> > -    if ifcond:
> > -        body += '\n\n@b{If:} @code{%s}' % ", ".join(ifcond)
> > +    body += texi_if(ifcond, suffix='')
> >      return body
> >
> >
> > diff --git a/tests/qapi-schema/doc-good.json b/tests/qapi-schema/doc-good.json
> > index 984cd8ed06..c7fe08c530 100644
> > --- a/tests/qapi-schema/doc-good.json
> > +++ b/tests/qapi-schema/doc-good.json
> > @@ -55,7 +55,9 @@
> >  #
> >  # @two is undocumented
> >  ##
> > -{ 'enum': 'Enum', 'data': [ 'one', 'two' ], 'if': 'defined(IFCOND)' }
> > +{ 'enum': 'Enum', 'data':
> > +  [ { 'name': 'one', 'if': 'defined(IFENUM)' }, 'two' ],
> > +  'if': 'defined(IFCOND)' }
>
> IFCOND applies to the enum, and IFENUM applies to one of its values.
> Awkward.  Let's rename IFENUM to IFONE.

ok

>
> >
> >  ##
> >  # @Base:
> > diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out
> > index c2fc5c774a..a05535b69b 100644
> > --- a/tests/qapi-schema/doc-good.out
> > +++ b/tests/qapi-schema/doc-good.out
> > @@ -11,6 +11,7 @@ enum QType
> >  module doc-good.json
> >  enum Enum
> >      member one
> > +        if ['defined(IFENUM)']
> >      member two
> >      if ['defined(IFCOND)']
> >  object Base
> > diff --git a/tests/qapi-schema/doc-good.texi b/tests/qapi-schema/doc-good.texi
> > index e42eace474..5c0231e0e6 100644
> > --- a/tests/qapi-schema/doc-good.texi
> > +++ b/tests/qapi-schema/doc-good.texi
> > @@ -84,12 +84,12 @@ Examples:
> >  @table @asis
> >  @item @code{one}
> >  The @emph{one} @{and only@}
> > +@*@b{If:} @code{defined(IFENUM)}
>
> @* forces a line break.  Putting it at the end of the previous line
> would be tidier, but let's ignore that for now.

For consistency and easier reading, I think it's better to keep it as
a seperate line in the end for the corresponding section

>
> >  @item @code{two}
> >  Not documented
> >  @end table
> >  @code{two} is undocumented
> >
> > -
> >  @b{If:} @code{defined(IFCOND)}
> >  @end deftp

thanks

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

* Re: [Qemu-devel] [PATCH for-4.0 v7 25/27] qapi: break long lines at 'data' member
  2018-12-11 16:07   ` Markus Armbruster
@ 2018-12-11 16:08     ` Marc-André Lureau
  0 siblings, 0 replies; 69+ messages in thread
From: Marc-André Lureau @ 2018-12-11 16:08 UTC (permalink / raw)
  To: Armbruster, Markus
  Cc: qemu-devel, Juan Quintela, Jason Wang, Dr. David Alan Gilbert,
	Hoffmann, Gerd, Bonzini, Paolo, stefanb

Hi

On Tue, Dec 11, 2018 at 8:07 PM Markus Armbruster <armbru@redhat.com> wrote:
>
> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
>
> > Let's break the line before 'data'. While at it, improve a bit
> > indentation/spacing. (I removed some alignment which are not helping
> > much readability and become quickly inconsistent)
> >
> > Suggested-by: Markus Armbruster <armbru@redhat.com>
> > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>
> Reviewed-by: Markus Armbruster <armbru@redhat.com>
>
> I can cherry-pick this one into my next pull request to reduce the
> likelihood of conflicts.

Ideally, you take as much patches as you can in your -next tree, so I
don't have to carry them and resend them all the time. Up to you, you
are the maintainer.

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

* Re: [Qemu-devel] [PATCH for-4.0 v7 26/27] qapi: add more conditions to SPICE
  2018-12-08 11:16 ` [Qemu-devel] [PATCH for-4.0 v7 26/27] qapi: add more conditions to SPICE Marc-André Lureau
@ 2018-12-11 17:11   ` Markus Armbruster
  0 siblings, 0 replies; 69+ messages in thread
From: Markus Armbruster @ 2018-12-11 17:11 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, Paolo Bonzini

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

> Now that member can be made conditional, let's make SPICE chardev
> conditional:
>
> * spiceport, spicevmc
>
>   Before and after the patch for !CONFIG_SPICE, the error is the
>   same ('spiceport' is not a valid char driver name).
>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  qapi/char.json | 16 ++++++++--------
>  1 file changed, 8 insertions(+), 8 deletions(-)
>
> diff --git a/qapi/char.json b/qapi/char.json
> index 24628331ec..77ed847972 100644
> --- a/qapi/char.json
> +++ b/qapi/char.json
> @@ -332,8 +332,8 @@
>  ##
>  { 'struct': 'ChardevSpiceChannel',
>    'data': { 'type': 'str' },
> -  'base': 'ChardevCommon' }
> -# TODO: 'if': 'defined(CONFIG_SPICE)'
> +  'base': 'ChardevCommon',
> +  'if': 'defined(CONFIG_SPICE)' }
>  
>  ##
>  # @ChardevSpicePort:
> @@ -346,8 +346,8 @@
>  ##
>  { 'struct': 'ChardevSpicePort',
>    'data': { 'fqdn': 'str' },
> -  'base': 'ChardevCommon' }
> -# TODO: 'if': 'defined(CONFIG_SPICE)'
> +  'base': 'ChardevCommon',
> +  'if': 'defined(CONFIG_SPICE)' }
>  
>  ##
>  # @ChardevVC:
> @@ -404,10 +404,10 @@
>              'testdev': 'ChardevCommon',
>              'stdio': 'ChardevStdio',
>              'console': 'ChardevCommon',
> -            'spicevmc': 'ChardevSpiceChannel',
> -# TODO: { 'type': 'ChardevSpiceChannel', 'if': 'defined(CONFIG_SPICE)' },
> -            'spiceport': 'ChardevSpicePort',
> -# TODO: { 'type': 'ChardevSpicePort', 'if': 'defined(CONFIG_SPICE)' },
> +            'spicevmc': { 'type': 'ChardevSpiceChannel',
> +                          'if': 'defined(CONFIG_SPICE)' },
> +            'spiceport': { 'type': 'ChardevSpicePort',
> +                           'if': 'defined(CONFIG_SPICE)' },
>              'vc': 'ChardevVC',
>              'ringbuf': 'ChardevRingbuf',
>              # next one is just for compatibility

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

Reviewing this patch's effect on generated files led me to a few things.
None of them need to be addressed to get this series accepted.  I
actually recommend not to address them in this series, because that
could lead to further delays.

There's an opportunity for minor output beautification: back-to-back
#endif / #if like

    #endif /* defined(CONFIG_SPICE) */
    #if defined(CONFIG_SPICE)

in generated C could be omitted.

The generated documentation shows conditionals as

    If: 'defined(CONFIG_SPICE)'

We should at least explain this notation in the introduction.  Other
notation, too.  Predates this series.

Should we *strip* documentation for disabled stuff instead?
Complication: disabled everywhere, or just for a target?  We build the
documentation only once, and that's a feature.  We can therefore only
strip documentation for stuff that's disabled target-independently.
That's a bit more involved.

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

* Re: [Qemu-devel] [PATCH for-4.0 v7 27/27] qapi: add conditions to REPLICATION type/commands on the schema
  2018-12-08 11:16 ` [Qemu-devel] [PATCH for-4.0 v7 27/27] qapi: add conditions to REPLICATION type/commands on the schema Marc-André Lureau
@ 2018-12-11 17:15   ` Markus Armbruster
  2018-12-11 17:17     ` Marc-André Lureau
  0 siblings, 1 reply; 69+ messages in thread
From: Markus Armbruster @ 2018-12-11 17:15 UTC (permalink / raw)
  To: Marc-André Lureau
  Cc: qemu-devel, zhanghailiang, Juan Quintela, Dr. David Alan Gilbert

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

> Add #if defined(CONFIG_REPLICATION) in generated code, and adjust the
> code accordingly.
>
> Made conditional:
>
> * xen-set-replication, query-xen-replication-status,
>   xen-colo-do-checkpoint
>
>   Before the patch, we first register the commands unconditionally in
>   generated code (requires a stub), then conditionally unregister in
>   qmp_unregister_commands_hack().
>
>   Afterwards, we register only when CONFIG_REPLICATION.  The command
>   fails exactly the same, with CommandNotFound.
>
>   Improvement, because now query-qmp-schema is accurate, and we're one
>   step closer to killing qmp_unregister_commands_hack().
>
> * enum BlockdevDriver value "replication" in command blockdev-add
>
> * BlockdevOptions variant @replication
>
> and related structures.
>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> Reviewed-by: Markus Armbruster <armbru@redhat.com>
> ---
>  qapi/block-core.json | 13 +++++++++----
>  qapi/migration.json  | 12 ++++++++----
>  migration/colo.c     | 16 ++++------------
>  monitor.c            |  5 -----
>  4 files changed, 21 insertions(+), 25 deletions(-)
>
> diff --git a/qapi/block-core.json b/qapi/block-core.json
> index 92e0205d91..762000f31f 100644
> --- a/qapi/block-core.json
> +++ b/qapi/block-core.json
> @@ -2623,7 +2623,9 @@
>              'copy-on-read', 'dmg', 'file', 'ftp', 'ftps', 'gluster',
>              'host_cdrom', 'host_device', 'http', 'https', 'iscsi', 'luks',
>              'nbd', 'nfs', 'null-aio', 'null-co', 'nvme', 'parallels', 'qcow',
> -            'qcow2', 'qed', 'quorum', 'raw', 'rbd', 'replication', 'sheepdog',
> +            'qcow2', 'qed', 'quorum', 'raw', 'rbd',
> +            { 'name': 'replication', 'if': 'defined(CONFIG_REPLICATION)' },
> +            'sheepdog',
>              'ssh', 'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] }
>  
>  ##
> @@ -3380,7 +3382,8 @@
>  #
>  # Since: 2.9
>  ##
> -{ 'enum' : 'ReplicationMode', 'data' : [ 'primary', 'secondary' ] }
> +{ 'enum' : 'ReplicationMode', 'data' : [ 'primary', 'secondary' ],
> +  'if': 'defined(CONFIG_REPLICATION)' }
>  
>  ##
>  # @BlockdevOptionsReplication:
> @@ -3398,7 +3401,8 @@
>  { 'struct': 'BlockdevOptionsReplication',
>    'base': 'BlockdevOptionsGenericFormat',
>    'data': { 'mode': 'ReplicationMode',
> -            '*top-id': 'str' } }
> +            '*top-id': 'str' },
> +  'if': 'defined(CONFIG_REPLICATION)' }
>  
>  ##
>  # @NFSTransport:
> @@ -3714,7 +3718,8 @@
>        'quorum':     'BlockdevOptionsQuorum',
>        'raw':        'BlockdevOptionsRaw',
>        'rbd':        'BlockdevOptionsRbd',
> -      'replication':'BlockdevOptionsReplication',
> +      'replication': { 'type': 'BlockdevOptionsReplication',
> +                       'if': 'defined(CONFIG_REPLICATION)' },
>        'sheepdog':   'BlockdevOptionsSheepdog',
>        'ssh':        'BlockdevOptionsSsh',
>        'throttle':   'BlockdevOptionsThrottle',
> diff --git a/qapi/migration.json b/qapi/migration.json
> index 5fd33316a0..31b589ec26 100644
> --- a/qapi/migration.json
> +++ b/qapi/migration.json
> @@ -1257,7 +1257,8 @@
>  # Since: 2.9
>  ##
>  { 'command': 'xen-set-replication',
> -  'data': { 'enable': 'bool', 'primary': 'bool', '*failover' : 'bool' } }
> +  'data': { 'enable': 'bool', 'primary': 'bool', '*failover' : 'bool' },
> +  'if': 'defined(CONFIG_REPLICATION)' }
>  
>  ##
>  # @ReplicationStatus:
> @@ -1272,7 +1273,8 @@
>  # Since: 2.9
>  ##
>  { 'struct': 'ReplicationStatus',
> -  'data': { 'error': 'bool', '*desc': 'str' } }
> +  'data': { 'error': 'bool', '*desc': 'str' },
> +  'if': 'defined(CONFIG_REPLICATION)' }
>  
>  ##
>  # @query-xen-replication-status:
> @@ -1289,7 +1291,8 @@
>  # Since: 2.9
>  ##
>  { 'command': 'query-xen-replication-status',
> -  'returns': 'ReplicationStatus' }
> +  'returns': 'ReplicationStatus',
> +  'if': 'defined(CONFIG_REPLICATION)' }
>  
>  ##
>  # @xen-colo-do-checkpoint:
> @@ -1305,7 +1308,8 @@
>  #
>  # Since: 2.9
>  ##
> -{ 'command': 'xen-colo-do-checkpoint' }
> +{ 'command': 'xen-colo-do-checkpoint',
> +  'if': 'defined(CONFIG_REPLICATION)' }
>  
>  ##
>  # @COLOStatus:
> diff --git a/migration/colo.c b/migration/colo.c
> index fcff04c78c..398b239d1c 100644
> --- a/migration/colo.c
> +++ b/migration/colo.c
> @@ -24,7 +24,9 @@
>  #include "trace.h"
>  #include "qemu/error-report.h"
>  #include "migration/failover.h"
> +#ifdef CONFIG_REPLICATION
>  #include "replication.h"
> +#endif
>  #include "net/colo-compare.h"
>  #include "net/colo.h"
>  #include "block/block.h"
> @@ -201,11 +203,11 @@ void colo_do_failover(MigrationState *s)
>      }
>  }
>  
> +#ifdef CONFIG_REPLICATION
>  void qmp_xen_set_replication(bool enable, bool primary,
>                               bool has_failover, bool failover,
>                               Error **errp)
>  {
> -#ifdef CONFIG_REPLICATION
>      ReplicationMode mode = primary ?
>                             REPLICATION_MODE_PRIMARY :
>                             REPLICATION_MODE_SECONDARY;
> @@ -224,14 +226,10 @@ void qmp_xen_set_replication(bool enable, bool primary,
>          }
>          replication_stop_all(failover, failover ? NULL : errp);
>      }
> -#else
> -    abort();
> -#endif
>  }
>  
>  ReplicationStatus *qmp_query_xen_replication_status(Error **errp)
>  {
> -#ifdef CONFIG_REPLICATION
>      Error *err = NULL;
>      ReplicationStatus *s = g_new0(ReplicationStatus, 1);
>  
> @@ -246,19 +244,13 @@ ReplicationStatus *qmp_query_xen_replication_status(Error **errp)
>  
>      error_free(err);
>      return s;
> -#else
> -    abort();
> -#endif
>  }
>  
>  void qmp_xen_colo_do_checkpoint(Error **errp)
>  {
> -#ifdef CONFIG_REPLICATION
>      replication_do_checkpoint_all(errp);
> -#else
> -    abort();
> -#endif
>  }
> +#endif

/* CONFIG_REPLICATION */ after the endif wouldn't hurt.

>  
>  COLOStatus *qmp_query_colo_status(Error **errp)
>  {
> diff --git a/monitor.c b/monitor.c
> index d39390c2f2..16a180dd50 100644
> --- a/monitor.c
> +++ b/monitor.c
> @@ -1146,11 +1146,6 @@ static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data,
>   */
>  static void qmp_unregister_commands_hack(void)
>  {
> -#ifndef CONFIG_REPLICATION
> -    qmp_unregister_command(&qmp_commands, "xen-set-replication");
> -    qmp_unregister_command(&qmp_commands, "query-xen-replication-status");
> -    qmp_unregister_command(&qmp_commands, "xen-colo-do-checkpoint");
> -#endif
>  #ifndef TARGET_I386
>      qmp_unregister_command(&qmp_commands, "rtc-reset-reinjection");
>      qmp_unregister_command(&qmp_commands, "query-sev");

Only TARGET conditionals left here.  Good.

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

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

* Re: [Qemu-devel] [PATCH for-4.0 v7 27/27] qapi: add conditions to REPLICATION type/commands on the schema
  2018-12-11 17:15   ` Markus Armbruster
@ 2018-12-11 17:17     ` Marc-André Lureau
  0 siblings, 0 replies; 69+ messages in thread
From: Marc-André Lureau @ 2018-12-11 17:17 UTC (permalink / raw)
  To: Armbruster, Markus
  Cc: qemu-devel, zhanghailiang, Juan Quintela, Dr. David Alan Gilbert

Hi

On Tue, Dec 11, 2018 at 9:15 PM Markus Armbruster <armbru@redhat.com> wrote:
>
> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
>
> > Add #if defined(CONFIG_REPLICATION) in generated code, and adjust the
> > code accordingly.
> >
> > Made conditional:
> >
> > * xen-set-replication, query-xen-replication-status,
> >   xen-colo-do-checkpoint
> >
> >   Before the patch, we first register the commands unconditionally in
> >   generated code (requires a stub), then conditionally unregister in
> >   qmp_unregister_commands_hack().
> >
> >   Afterwards, we register only when CONFIG_REPLICATION.  The command
> >   fails exactly the same, with CommandNotFound.
> >
> >   Improvement, because now query-qmp-schema is accurate, and we're one
> >   step closer to killing qmp_unregister_commands_hack().
> >
> > * enum BlockdevDriver value "replication" in command blockdev-add
> >
> > * BlockdevOptions variant @replication
> >
> > and related structures.
> >
> > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> > Reviewed-by: Markus Armbruster <armbru@redhat.com>
> > ---
> >  qapi/block-core.json | 13 +++++++++----
> >  qapi/migration.json  | 12 ++++++++----
> >  migration/colo.c     | 16 ++++------------
> >  monitor.c            |  5 -----
> >  4 files changed, 21 insertions(+), 25 deletions(-)
> >
> > diff --git a/qapi/block-core.json b/qapi/block-core.json
> > index 92e0205d91..762000f31f 100644
> > --- a/qapi/block-core.json
> > +++ b/qapi/block-core.json
> > @@ -2623,7 +2623,9 @@
> >              'copy-on-read', 'dmg', 'file', 'ftp', 'ftps', 'gluster',
> >              'host_cdrom', 'host_device', 'http', 'https', 'iscsi', 'luks',
> >              'nbd', 'nfs', 'null-aio', 'null-co', 'nvme', 'parallels', 'qcow',
> > -            'qcow2', 'qed', 'quorum', 'raw', 'rbd', 'replication', 'sheepdog',
> > +            'qcow2', 'qed', 'quorum', 'raw', 'rbd',
> > +            { 'name': 'replication', 'if': 'defined(CONFIG_REPLICATION)' },
> > +            'sheepdog',
> >              'ssh', 'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] }
> >
> >  ##
> > @@ -3380,7 +3382,8 @@
> >  #
> >  # Since: 2.9
> >  ##
> > -{ 'enum' : 'ReplicationMode', 'data' : [ 'primary', 'secondary' ] }
> > +{ 'enum' : 'ReplicationMode', 'data' : [ 'primary', 'secondary' ],
> > +  'if': 'defined(CONFIG_REPLICATION)' }
> >
> >  ##
> >  # @BlockdevOptionsReplication:
> > @@ -3398,7 +3401,8 @@
> >  { 'struct': 'BlockdevOptionsReplication',
> >    'base': 'BlockdevOptionsGenericFormat',
> >    'data': { 'mode': 'ReplicationMode',
> > -            '*top-id': 'str' } }
> > +            '*top-id': 'str' },
> > +  'if': 'defined(CONFIG_REPLICATION)' }
> >
> >  ##
> >  # @NFSTransport:
> > @@ -3714,7 +3718,8 @@
> >        'quorum':     'BlockdevOptionsQuorum',
> >        'raw':        'BlockdevOptionsRaw',
> >        'rbd':        'BlockdevOptionsRbd',
> > -      'replication':'BlockdevOptionsReplication',
> > +      'replication': { 'type': 'BlockdevOptionsReplication',
> > +                       'if': 'defined(CONFIG_REPLICATION)' },
> >        'sheepdog':   'BlockdevOptionsSheepdog',
> >        'ssh':        'BlockdevOptionsSsh',
> >        'throttle':   'BlockdevOptionsThrottle',
> > diff --git a/qapi/migration.json b/qapi/migration.json
> > index 5fd33316a0..31b589ec26 100644
> > --- a/qapi/migration.json
> > +++ b/qapi/migration.json
> > @@ -1257,7 +1257,8 @@
> >  # Since: 2.9
> >  ##
> >  { 'command': 'xen-set-replication',
> > -  'data': { 'enable': 'bool', 'primary': 'bool', '*failover' : 'bool' } }
> > +  'data': { 'enable': 'bool', 'primary': 'bool', '*failover' : 'bool' },
> > +  'if': 'defined(CONFIG_REPLICATION)' }
> >
> >  ##
> >  # @ReplicationStatus:
> > @@ -1272,7 +1273,8 @@
> >  # Since: 2.9
> >  ##
> >  { 'struct': 'ReplicationStatus',
> > -  'data': { 'error': 'bool', '*desc': 'str' } }
> > +  'data': { 'error': 'bool', '*desc': 'str' },
> > +  'if': 'defined(CONFIG_REPLICATION)' }
> >
> >  ##
> >  # @query-xen-replication-status:
> > @@ -1289,7 +1291,8 @@
> >  # Since: 2.9
> >  ##
> >  { 'command': 'query-xen-replication-status',
> > -  'returns': 'ReplicationStatus' }
> > +  'returns': 'ReplicationStatus',
> > +  'if': 'defined(CONFIG_REPLICATION)' }
> >
> >  ##
> >  # @xen-colo-do-checkpoint:
> > @@ -1305,7 +1308,8 @@
> >  #
> >  # Since: 2.9
> >  ##
> > -{ 'command': 'xen-colo-do-checkpoint' }
> > +{ 'command': 'xen-colo-do-checkpoint',
> > +  'if': 'defined(CONFIG_REPLICATION)' }
> >
> >  ##
> >  # @COLOStatus:
> > diff --git a/migration/colo.c b/migration/colo.c
> > index fcff04c78c..398b239d1c 100644
> > --- a/migration/colo.c
> > +++ b/migration/colo.c
> > @@ -24,7 +24,9 @@
> >  #include "trace.h"
> >  #include "qemu/error-report.h"
> >  #include "migration/failover.h"
> > +#ifdef CONFIG_REPLICATION
> >  #include "replication.h"
> > +#endif
> >  #include "net/colo-compare.h"
> >  #include "net/colo.h"
> >  #include "block/block.h"
> > @@ -201,11 +203,11 @@ void colo_do_failover(MigrationState *s)
> >      }
> >  }
> >
> > +#ifdef CONFIG_REPLICATION
> >  void qmp_xen_set_replication(bool enable, bool primary,
> >                               bool has_failover, bool failover,
> >                               Error **errp)
> >  {
> > -#ifdef CONFIG_REPLICATION
> >      ReplicationMode mode = primary ?
> >                             REPLICATION_MODE_PRIMARY :
> >                             REPLICATION_MODE_SECONDARY;
> > @@ -224,14 +226,10 @@ void qmp_xen_set_replication(bool enable, bool primary,
> >          }
> >          replication_stop_all(failover, failover ? NULL : errp);
> >      }
> > -#else
> > -    abort();
> > -#endif
> >  }
> >
> >  ReplicationStatus *qmp_query_xen_replication_status(Error **errp)
> >  {
> > -#ifdef CONFIG_REPLICATION
> >      Error *err = NULL;
> >      ReplicationStatus *s = g_new0(ReplicationStatus, 1);
> >
> > @@ -246,19 +244,13 @@ ReplicationStatus *qmp_query_xen_replication_status(Error **errp)
> >
> >      error_free(err);
> >      return s;
> > -#else
> > -    abort();
> > -#endif
> >  }
> >
> >  void qmp_xen_colo_do_checkpoint(Error **errp)
> >  {
> > -#ifdef CONFIG_REPLICATION
> >      replication_do_checkpoint_all(errp);
> > -#else
> > -    abort();
> > -#endif
> >  }
> > +#endif
>
> /* CONFIG_REPLICATION */ after the endif wouldn't hurt.
>
> >
> >  COLOStatus *qmp_query_colo_status(Error **errp)
> >  {
> > diff --git a/monitor.c b/monitor.c
> > index d39390c2f2..16a180dd50 100644
> > --- a/monitor.c
> > +++ b/monitor.c
> > @@ -1146,11 +1146,6 @@ static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data,
> >   */
> >  static void qmp_unregister_commands_hack(void)
> >  {
> > -#ifndef CONFIG_REPLICATION
> > -    qmp_unregister_command(&qmp_commands, "xen-set-replication");
> > -    qmp_unregister_command(&qmp_commands, "query-xen-replication-status");
> > -    qmp_unregister_command(&qmp_commands, "xen-colo-do-checkpoint");
> > -#endif
> >  #ifndef TARGET_I386
> >      qmp_unregister_command(&qmp_commands, "rtc-reset-reinjection");
> >      qmp_unregister_command(&qmp_commands, "query-sev");
>
> Only TARGET conditionals left here.  Good.

IIRC, they are removed in part 3.

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

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

* Re: [Qemu-devel] [PATCH for-4.0 v7 01/27] qapi: make sure osdep.h is included in type headers
  2018-12-11 15:47         ` Marc-André Lureau
@ 2018-12-12  6:47           ` Markus Armbruster
  0 siblings, 0 replies; 69+ messages in thread
From: Markus Armbruster @ 2018-12-12  6:47 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: QEMU, Michael Roth

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

> Hi
>
> On Mon, Dec 10, 2018 at 5:28 PM Markus Armbruster <armbru@redhat.com> wrote:
>>
>> Marc-André Lureau <marcandre.lureau@gmail.com> writes:
>>
>> > Hi
>> >
>> > On Mon, Dec 10, 2018 at 1:52 PM Markus Armbruster <armbru@redhat.com> wrote:
>> >>
>> >> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
>> >>
>> >> > Now that the schema can be configured, it is crucial that all types
>> >> > are configured the same. Make sure config-host.h is included, by
>> >> > checking osdep.h inclusion. The build-sys tracks the dependency and
>> >> > rebuilds the types if the configuration changed.
>> >> >
>> >> > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>> >> > ---
>> >> >  scripts/qapi/types.py | 3 +++
>> >> >  1 file changed, 3 insertions(+)
>> >> >
>> >> > diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py
>> >> > index fd7808103c..3bb9cb6d47 100644
>> >> > --- a/scripts/qapi/types.py
>> >> > +++ b/scripts/qapi/types.py
>> >> > @@ -201,6 +201,9 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor):
>> >> >  ''',
>> >> >                                        types=types, visit=visit))
>> >> >          self._genh.preamble_add(mcgen('''
>> >> > +#ifndef QEMU_OSDEP_H
>> >> > +#error Please include osdep.h first!
>> >> > +#endif
>> >> >  #include "qapi/qapi-builtin-types.h"
>> >> >  '''))
>> >>
>> >> I understand why you propose this patch.  The QAPI-generated headers use
>> >> #ifdef CONFIG_FOO.  The configuration header "qemu/osdep.h" must be
>> >> consistently included before the generated headers, or else you end up
>> >> with nasty bugs, such as the same enum having different values in
>> >> different .o, or the same struct having a different layout.
>> >>
>> >> But this applies to *all* headers that use #ifdef.  Why check it here,
>> >> but not there?  What makes the QAPI-generated headers special?
>> >>
>> >
>> > It's the discussion about #if in headers (and enums in particular)
>> > that started this. We want to make sure all compilation units share
>> > the same data structure/ABI. I proposed to include osdep.h in qapi
>> > headers, but that was rejected.
>> > The warning is a different approach. I agree it could apply to all
>> > headers. Do you think I should update all headers as well?
>>
>> That would replace the rule 'all .c files must include "qemu/osdep.h"
>> first' by 'all .h files must check "qemu/osdep.h" has been included
>> already', which is not an improvement.
>
> That would be an improvement since headers may also be impacted by osdep.h
>
> Alternatively, why not use -include ?

Requiring .c to include the configuration header first follows
established Autoconf practice.  The "first" part covers all headers.

I've seen plenty of autoconfiscated code, yet none where the headers
double-check the configuration header has been included already.  Such a
check is neither sufficient nor really necessary.  Checking the .c is
both.

I have no strong feelings about using -include instead.  I'd prefer to
avoid the difference to what other projects do, though.

Circling back to the issue that made you propose this patch.

I think I'm (belatedly) coming around to your initial view that the use
of conditionals in generated QAPI code is safe enough.

I shouldn't be worried about #if defined(CONFIG_HOST_THING) where
CONFIG_HOST_THING belongs to config-host.h.  That's always pulled in via
qemu/osdep.h.

I also shouldn't be worried about #if defined(CONFIG_TARGET_THING) where
CONFIG_TARGET_THING belongs to config-target.h.  qemu/osdep.h pulls in
either that or exec/poison.h.

I even shouldn't be worried about #if defined(RANDOM_MACRO) as long as
qemu/osdep.h pulls in its owner or poisons it.  I might legitimately be
worried about the "as long as" part.

We could perhaps extract the ifconds and look for macros that belong
neither to config-host.h nor config-target.h.  But I'm not sure it's
worth the bother.

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

* Re: [Qemu-devel] [PATCH for-4.0 v7 01/27] qapi: make sure osdep.h is included in type headers
  2018-12-11 16:05         ` Daniel P. Berrangé
@ 2018-12-12  6:48           ` Markus Armbruster
  0 siblings, 0 replies; 69+ messages in thread
From: Markus Armbruster @ 2018-12-12  6:48 UTC (permalink / raw)
  To: Daniel P. Berrangé; +Cc: Marc-André Lureau, QEMU, Michael Roth

Daniel P. Berrangé <berrange@redhat.com> writes:

> On Mon, Dec 10, 2018 at 02:28:26PM +0100, Markus Armbruster wrote:
[...]
>> checkpatch.pl could flag patches adding .c files that don't include
>> "qemu/osdep.h" first.  The "first" part might be a bit annoying to code.
>
> You can get this logic from GNULIBs syntax-check make rules. It uses
> this perl code to check that config.h is included first:
>
>     while (<>) {
>         if (/^# *include\b/) {
>             if (not m{^# *include <config\.h>}) {
>                 print "$ARGV\n";
>                 $$ret = 1;
>             }
>             close ARGV;
>         }
>     }

Thanks!

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

* Re: [Qemu-devel] [PATCH for-4.0 v7 02/27] qapi: do not define enumeration value explicitly
  2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 02/27] qapi: do not define enumeration value explicitly Marc-André Lureau
@ 2018-12-12  8:52   ` Markus Armbruster
  2018-12-12  9:05     ` Marc-André Lureau
  0 siblings, 1 reply; 69+ messages in thread
From: Markus Armbruster @ 2018-12-12  8:52 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel, Michael Roth

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

> The C standard has the initial value at 0 and the subsequent values
> incremented by 1. No need to set this explicitely.
>
> This will prevent from artificial "gaps" when compiling out some enum
> values and having unnecessarily large MAX values & enums arrays, or
> simplifying iterating over valid enum values.

Should mention that compiling out will only become possible later in
this series.

> Whenever config-host.h is changed, all the enum/types are recompiled.

This soundness argument is incomplete.  Yes, our coding conventions
ensure everything gets recompiled when config-host.h changes.  But
nothing stops people from using 'if' conditions that depend on more than
just config-host.h.

What about this:

  qapi: Do not define enumeration value explicitly

  The generated C enumeration types explicitly set the enumeration
  constants to 0, 1, 2, ...  That's exactly what you get when you don't
  supply values.

  Drop the explicit values.  No change now, but it will avoid gaps in
  the values when we later add support for 'if' conditions.  Avoiding
  such gaps will save us the trouble of changing the ENUM_lookup[]
  tables to work without a sentinel.

  We'll have to take care to ensure the headers required by the 'if'
  conditions get always included before the generated QAPI code.
  Fortunately, our convention to include "qemu/osdep.h" first in any .c
  ensures that's the case for our CONFIG_FOO macros.

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

With an improved commit message:
Reviewed-by: Markus Armbruster <armbru@redhat.com>

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

* Re: [Qemu-devel] [PATCH for-4.0 v7 02/27] qapi: do not define enumeration value explicitly
  2018-12-12  8:52   ` Markus Armbruster
@ 2018-12-12  9:05     ` Marc-André Lureau
  0 siblings, 0 replies; 69+ messages in thread
From: Marc-André Lureau @ 2018-12-12  9:05 UTC (permalink / raw)
  To: Armbruster, Markus; +Cc: qemu-devel, Michael Roth

Hi

On Wed, Dec 12, 2018 at 12:52 PM Markus Armbruster <armbru@redhat.com> wrote:
>
> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
>
> > The C standard has the initial value at 0 and the subsequent values
> > incremented by 1. No need to set this explicitely.
> >
> > This will prevent from artificial "gaps" when compiling out some enum
> > values and having unnecessarily large MAX values & enums arrays, or
> > simplifying iterating over valid enum values.
>
> Should mention that compiling out will only become possible later in
> this series.
>
> > Whenever config-host.h is changed, all the enum/types are recompiled.
>
> This soundness argument is incomplete.  Yes, our coding conventions
> ensure everything gets recompiled when config-host.h changes.  But
> nothing stops people from using 'if' conditions that depend on more than
> just config-host.h.
>
> What about this:
>
>   qapi: Do not define enumeration value explicitly
>
>   The generated C enumeration types explicitly set the enumeration
>   constants to 0, 1, 2, ...  That's exactly what you get when you don't
>   supply values.
>
>   Drop the explicit values.  No change now, but it will avoid gaps in
>   the values when we later add support for 'if' conditions.  Avoiding
>   such gaps will save us the trouble of changing the ENUM_lookup[]
>   tables to work without a sentinel.
>
>   We'll have to take care to ensure the headers required by the 'if'
>   conditions get always included before the generated QAPI code.
>   Fortunately, our convention to include "qemu/osdep.h" first in any .c
>   ensures that's the case for our CONFIG_FOO macros.
>
> > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>
> With an improved commit message:
> Reviewed-by: Markus Armbruster <armbru@redhat.com>

ack, thanks!

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

* Re: [Qemu-devel] [PATCH for-4.0 v7 00/27] Hi,
  2018-12-08 11:15 [Qemu-devel] [PATCH for-4.0 v7 00/27] Hi, Marc-André Lureau
                   ` (27 preceding siblings ...)
  2018-12-08 11:19 ` [Qemu-devel] [PATCH for-4.0 v7 00/27] Hi, Marc-André Lureau
@ 2018-12-12 17:18 ` Markus Armbruster
  2018-12-12 18:13   ` Marc-André Lureau
  28 siblings, 1 reply; 69+ messages in thread
From: Markus Armbruster @ 2018-12-12 17:18 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: qemu-devel

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

> This is the second part of the "add #if pre-processor conditions to
> generated code" series, adding schema member conditions (roughly
> 16-38/49).
>
> Members can be exploded as dictionnary with 'type'/'if' keys:
>
> { 'struct': 'TestIfStruct', 'data':
>   { 'foo': 'int',
>     'bar': { 'type': 'int', 'if': 'defined(TEST_IF_STRUCT_BAR)'} } }
>
> Enum values can be exploded as dictionnary with 'type'/'if' keys:
>
> { 'enum': 'TestIfEnum', 'data':
>   [ 'foo',
>     { 'name' : 'bar', 'if': 'defined(TEST_IF_ENUM_BAR)' } ] }

PATCH 03,18,20,25 are only losely related to this second part.  They are
also ready.  Queued on branch qapi-next in my public repository, with
PATCH 18 and 20 squashed together.

I took the liberty to implement the the idea on normalization I
mentioned in review of PATCH 08.  I also squashed a few patches that are
essentially fixups into the patches they fix.  I did not address any
other review comments.  Result is on branch qapi-if-2.  Please have a
look.

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

* Re: [Qemu-devel] [PATCH for-4.0 v7 00/27] Hi,
  2018-12-12 17:18 ` Markus Armbruster
@ 2018-12-12 18:13   ` Marc-André Lureau
  2018-12-13  5:11     ` Markus Armbruster
  0 siblings, 1 reply; 69+ messages in thread
From: Marc-André Lureau @ 2018-12-12 18:13 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: QEMU

Hi

On Wed, Dec 12, 2018 at 9:18 PM Markus Armbruster <armbru@redhat.com> wrote:
>
> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
>
> > This is the second part of the "add #if pre-processor conditions to
> > generated code" series, adding schema member conditions (roughly
> > 16-38/49).
> >
> > Members can be exploded as dictionnary with 'type'/'if' keys:
> >
> > { 'struct': 'TestIfStruct', 'data':
> >   { 'foo': 'int',
> >     'bar': { 'type': 'int', 'if': 'defined(TEST_IF_STRUCT_BAR)'} } }
> >
> > Enum values can be exploded as dictionnary with 'type'/'if' keys:
> >
> > { 'enum': 'TestIfEnum', 'data':
> >   [ 'foo',
> >     { 'name' : 'bar', 'if': 'defined(TEST_IF_ENUM_BAR)' } ] }
>
> PATCH 03,18,20,25 are only losely related to this second part.  They are
> also ready.  Queued on branch qapi-next in my public repository, with
> PATCH 18 and 20 squashed together.
>
> I took the liberty to implement the the idea on normalization I
> mentioned in review of PATCH 08.  I also squashed a few patches that are
> essentially fixups into the patches they fix.  I did not address any
> other review comments.  Result is on branch qapi-if-2.  Please have a
> look.
>

Looks good to me.  Is there something left to rework from this branch
or it is good to go?
thanks

-- 
Marc-André Lureau

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

* Re: [Qemu-devel] [PATCH for-4.0 v7 00/27] Hi,
  2018-12-12 18:13   ` Marc-André Lureau
@ 2018-12-13  5:11     ` Markus Armbruster
  0 siblings, 0 replies; 69+ messages in thread
From: Markus Armbruster @ 2018-12-13  5:11 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: Markus Armbruster, QEMU

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

> Hi
>
> On Wed, Dec 12, 2018 at 9:18 PM Markus Armbruster <armbru@redhat.com> wrote:
>>
>> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
>>
>> > This is the second part of the "add #if pre-processor conditions to
>> > generated code" series, adding schema member conditions (roughly
>> > 16-38/49).
>> >
>> > Members can be exploded as dictionnary with 'type'/'if' keys:
>> >
>> > { 'struct': 'TestIfStruct', 'data':
>> >   { 'foo': 'int',
>> >     'bar': { 'type': 'int', 'if': 'defined(TEST_IF_STRUCT_BAR)'} } }
>> >
>> > Enum values can be exploded as dictionnary with 'type'/'if' keys:
>> >
>> > { 'enum': 'TestIfEnum', 'data':
>> >   [ 'foo',
>> >     { 'name' : 'bar', 'if': 'defined(TEST_IF_ENUM_BAR)' } ] }
>>
>> PATCH 03,18,20,25 are only losely related to this second part.  They are
>> also ready.  Queued on branch qapi-next in my public repository, with
>> PATCH 18 and 20 squashed together.
>>
>> I took the liberty to implement the the idea on normalization I
>> mentioned in review of PATCH 08.  I also squashed a few patches that are
>> essentially fixups into the patches they fix.  I did not address any
>> other review comments.  Result is on branch qapi-if-2.  Please have a
>> look.
>>
>
> Looks good to me.  Is there something left to rework from this branch
> or it is good to go?
> thanks

I adressed only the one review comment that was hard to explain, plus
some patch squashing.  The other review comments still need to be
considered and worked in.

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

* Re: [Qemu-devel] [PATCH for-4.0 v7 22/27] qapi: add 'If:' condition to enum values documentation
  2018-12-11 16:07     ` Marc-André Lureau
@ 2018-12-13 13:59       ` Markus Armbruster
  0 siblings, 0 replies; 69+ messages in thread
From: Markus Armbruster @ 2018-12-13 13:59 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: Armbruster, Markus, qemu-devel, Michael Roth

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

> Hi
>
> On Tue, Dec 11, 2018 at 8:00 PM Markus Armbruster <armbru@redhat.com> wrote:
>>
>> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
>>
>> > Use a common function to generate the "If:..." line.
>> >
>> > While at it, get rid of the existing \n\n (no idea why it was
>> > there). Use a line-break in member description, this seems to look
>> > slightly better in the plaintext version.
>>
>> Where exactly in the patch is this done?
>
> -    if ifcond:
> -        body += '\n\n@b{If:} @code{%s}' % ", ".join(ifcond)
> +    body += texi_if(ifcond, suffix='')

Thanks.  I suspected that much, but wasn't sure.

> See also doc-good.texi output
>
>>
>> > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>> > ---
>> >  scripts/qapi/doc.py             | 24 +++++++++++++++---------
>> >  tests/qapi-schema/doc-good.json |  4 +++-
>> >  tests/qapi-schema/doc-good.out  |  1 +
>> >  tests/qapi-schema/doc-good.texi |  2 +-
>> >  4 files changed, 20 insertions(+), 11 deletions(-)
>> >
>> > diff --git a/scripts/qapi/doc.py b/scripts/qapi/doc.py
>> > index 76cb186ff9..2133ded47e 100755
>> > --- a/scripts/qapi/doc.py
>> > +++ b/scripts/qapi/doc.py
>> > @@ -126,19 +126,26 @@ def texi_body(doc):
>> >      return texi_format(doc.body.text)
>> >
>> >
>> > -def texi_enum_value(value):
>> > +def texi_if(ifcond, prefix='\n', suffix='\n'):
>> > +    """Format the #if condition"""
>> > +    return '%s@b{If:} @code{%s}%s' % (
>> > +        prefix, ', '.join(ifcond), suffix) if ifcond else ''
>>
>> I like the ternary operator, but I nevertheless think this function is
>> easier to read as
>>
>>        if not ifcond:
>>            return ''
>>        return '%s@b{If:} @code{%s}%s' % (prefix, ', '.join(ifcond), suffix)
>>
>
> No difference to me, sure
>
>> > +
>> > +
>> > +def texi_enum_value(value, desc, suffix=''):
>> >      """Format a table of members item for an enumeration value"""
>> > -    return '@item @code{%s}\n' % value.name
>> > +    return '@item @code{%s}\n%s%s' % (
>> > +        value.name, desc, texi_if(value.ifcond, prefix='@*'))
>>
>> Do you ignore suffix intentionally?
>
> yes

That's kind of surprising, until you pry open the abstraction and
realize that we reach this one only from the first call of member_func()
below, which ...

>>
>> >
>> >
>> > -def texi_member(member, suffix=''):
>> > +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' % (
>> > +    return '@item @code{%s%s}%s%s\n%s' % (
>> >          member.name, membertype,
>> >          ' (optional)' if member.optional else '',
>> > -        suffix)
>> > +        suffix, desc)
>> >
>> >
>> >  def texi_members(doc, what, base, variants, member_func):
>> > @@ -155,7 +162,7 @@ def texi_members(doc, what, base, variants, member_func):
>> >              desc = 'One of ' + members_text + '\n'
>> >          else:
>> >              desc = 'Not documented\n'
>> > -        items += member_func(section.member) + desc
>> > +        items += member_func(section.member, desc)
>>
>> Here, you pass a @desc argument, but no @suffix.
>>
>> @member_func is either texi_enum_value() or texi_member().  Works.

... doesn't pass @suffix.  So @suffix is empty when we ignore it.

>> >      if base:
>> >          items += '@item The members of @code{%s}\n' % base.doc_type()
>> >      if variants:
>> > @@ -165,7 +172,7 @@ def texi_members(doc, what, base, variants, member_func):
>> >              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, when)
>> > +                    items += member_func(m, suffix=when)
>>
>> Here, you pass a @suffix argument, but no @desc.
>>
>> texi_enum_value() would choke on that, but it can't occur here, because
>> enums have no variants.
>>
>> Still, I'd prefer texi_enum_value() and texi_member() to have the exact
>> same signature.
>
> OK (not sure what is the more pythonic way)

I suspect there's a clean solution struggling to get out.  But I can't
grasp it yet.

>>
>> >              else:
>> >                  items += '@item The members of @code{%s}%s\n' % (
>> >                      v.type.doc_type(), when)
>> > @@ -185,8 +192,7 @@ def texi_sections(doc, ifcond):
>> >              body += texi_example(section.text)
>> >          else:
>> >              body += texi_format(section.text)
>> > -    if ifcond:
>> > -        body += '\n\n@b{If:} @code{%s}' % ", ".join(ifcond)
>> > +    body += texi_if(ifcond, suffix='')
>> >      return body
>> >
>> >
[...]

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

end of thread, other threads:[~2018-12-13 13:59 UTC | newest]

Thread overview: 69+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-12-08 11:15 [Qemu-devel] [PATCH for-4.0 v7 00/27] Hi, Marc-André Lureau
2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 01/27] qapi: make sure osdep.h is included in type headers Marc-André Lureau
2018-12-10  9:52   ` Markus Armbruster
2018-12-10 11:13     ` Marc-André Lureau
2018-12-10 13:28       ` Markus Armbruster
2018-12-11 15:47         ` Marc-André Lureau
2018-12-12  6:47           ` Markus Armbruster
2018-12-11 16:05         ` Daniel P. Berrangé
2018-12-12  6:48           ` Markus Armbruster
2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 02/27] qapi: do not define enumeration value explicitly Marc-André Lureau
2018-12-12  8:52   ` Markus Armbruster
2018-12-12  9:05     ` Marc-André Lureau
2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 03/27] qapi: rename QAPISchemaEnumType.values to .members Marc-André Lureau
2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 04/27] qapi: change enum visitor and gen_enum* to take QAPISchemaMember Marc-André Lureau
2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 05/27] tests: print enum type members more like object type members Marc-André Lureau
2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 06/27] qapi: factor out checking for keys Marc-André Lureau
2018-12-10  9:57   ` Markus Armbruster
2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 07/27] qapi: improve reporting of unknown or missing keys Marc-André Lureau
2018-12-10 10:03   ` Markus Armbruster
2018-12-10 11:11     ` Marc-André Lureau
2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 08/27] qapi: add a dictionary form with 'name' key for enum members Marc-André Lureau
2018-12-10 15:44   ` Markus Armbruster
2018-12-10 16:05     ` Markus Armbruster
2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 09/27] qapi: add 'if' to " Marc-André Lureau
2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 10/27] qapi-events: add 'if' condition to implicit event enum Marc-André Lureau
2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 11/27] qapi: pass long form enum to make_enum_members Marc-André Lureau
2018-12-10 17:04   ` Markus Armbruster
2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 12/27] qapi: rename allow_dict to allow_implicit Marc-André Lureau
2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 13/27] qapi: add a dictionary form for TYPE Marc-André Lureau
2018-12-10 17:24   ` Markus Armbruster
2018-12-11 12:11     ` Marc-André Lureau
2018-12-11 15:35       ` Markus Armbruster
2018-12-11 15:18     ` Markus Armbruster
2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 14/27] qapi: add 'if' to implicit struct members Marc-André Lureau
2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 15/27] qapi: add an error in case a discriminator is conditional Marc-André Lureau
2018-12-10 17:31   ` Markus Armbruster
2018-12-11 15:23     ` Markus Armbruster
2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 16/27] qapi: add 'if' to union members Marc-André Lureau
2018-12-10 17:33   ` Markus Armbruster
2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 17/27] qapi: simplify make_enum_members() Marc-André Lureau
2018-12-11 12:05   ` Markus Armbruster
2018-12-11 12:08     ` Marc-André Lureau
2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 18/27] tests/qapi: add command with condition on union argument Marc-André Lureau
2018-12-11 12:09   ` Markus Armbruster
2018-12-11 12:13     ` Marc-André Lureau
2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 19/27] qapi: add 'if' to alternate members Marc-André Lureau
2018-12-11 12:21   ` Markus Armbruster
2018-12-08 11:15 ` [Qemu-devel] [PATCH for-4.0 v7 20/27] tests/qapi: add command with condition on alternate argument Marc-André Lureau
2018-12-11 12:20   ` Markus Armbruster
2018-12-08 11:16 ` [Qemu-devel] [PATCH for-4.0 v7 21/27] qapi: add #if conditions to generated code members Marc-André Lureau
2018-12-11 13:17   ` Markus Armbruster
2018-12-08 11:16 ` [Qemu-devel] [PATCH for-4.0 v7 22/27] qapi: add 'If:' condition to enum values documentation Marc-André Lureau
2018-12-11 16:00   ` Markus Armbruster
2018-12-11 16:07     ` Marc-André Lureau
2018-12-13 13:59       ` Markus Armbruster
2018-12-08 11:16 ` [Qemu-devel] [PATCH for-4.0 v7 23/27] qapi: add 'If:' condition to struct members documentation Marc-André Lureau
2018-12-08 11:16 ` [Qemu-devel] [PATCH for-4.0 v7 24/27] qapi: add condition to variants documentation Marc-André Lureau
2018-12-08 11:16 ` [Qemu-devel] [PATCH for-4.0 v7 25/27] qapi: break long lines at 'data' member Marc-André Lureau
2018-12-11 16:07   ` Markus Armbruster
2018-12-11 16:08     ` Marc-André Lureau
2018-12-08 11:16 ` [Qemu-devel] [PATCH for-4.0 v7 26/27] qapi: add more conditions to SPICE Marc-André Lureau
2018-12-11 17:11   ` Markus Armbruster
2018-12-08 11:16 ` [Qemu-devel] [PATCH for-4.0 v7 27/27] qapi: add conditions to REPLICATION type/commands on the schema Marc-André Lureau
2018-12-11 17:15   ` Markus Armbruster
2018-12-11 17:17     ` Marc-André Lureau
2018-12-08 11:19 ` [Qemu-devel] [PATCH for-4.0 v7 00/27] Hi, Marc-André Lureau
2018-12-12 17:18 ` Markus Armbruster
2018-12-12 18:13   ` Marc-André Lureau
2018-12-13  5:11     ` 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.