new file mode 100644
new file mode 100644
@@ -0,0 +1,135 @@
+"""
+Golang QAPI generator
+"""
+
+# Copyright (c) 2025 Red Hat Inc.
+#
+# Authors:
+# Victor Toso <victortoso@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2.
+# See the COPYING file in the top-level directory.
+
+# Just for type hint on self
+from __future__ import annotations
+
+import os, shutil
+from typing import List, Optional
+
+from ..schema import (
+ QAPISchema,
+ QAPISchemaBranches,
+ QAPISchemaEnumMember,
+ QAPISchemaFeature,
+ QAPISchemaIfCond,
+ QAPISchemaObjectType,
+ QAPISchemaObjectTypeMember,
+ QAPISchemaType,
+ QAPISchemaVariants,
+ QAPISchemaVisitor,
+)
+from ..source import QAPISourceInfo
+
+
+def gen_golang(schema: QAPISchema, output_dir: str, prefix: str) -> None:
+ vis = QAPISchemaGenGolangVisitor(prefix)
+ schema.visit(vis)
+ vis.write(output_dir)
+
+
+class QAPISchemaGenGolangVisitor(QAPISchemaVisitor):
+ # pylint: disable=too-many-arguments
+ def __init__(self, _: str):
+ super().__init__()
+ gofiles = ("protocol.go",)
+ self.schema: QAPISchema
+ self.golang_package_name = "qapi"
+ self.duplicate = list(gofiles)
+
+ def visit_begin(self, schema: QAPISchema) -> None:
+ self.schema = schema
+
+ def visit_end(self) -> None:
+ del self.schema
+
+ def visit_object_type(
+ self,
+ name: str,
+ info: Optional[QAPISourceInfo],
+ ifcond: QAPISchemaIfCond,
+ features: List[QAPISchemaFeature],
+ base: Optional[QAPISchemaObjectType],
+ members: List[QAPISchemaObjectTypeMember],
+ branches: Optional[QAPISchemaBranches],
+ ) -> None:
+ pass
+
+ def visit_alternate_type(
+ self,
+ name: str,
+ info: Optional[QAPISourceInfo],
+ ifcond: QAPISchemaIfCond,
+ features: List[QAPISchemaFeature],
+ variants: QAPISchemaVariants,
+ ) -> None:
+ pass
+
+ def visit_enum_type(
+ self,
+ name: str,
+ info: Optional[QAPISourceInfo],
+ ifcond: QAPISchemaIfCond,
+ features: List[QAPISchemaFeature],
+ members: List[QAPISchemaEnumMember],
+ prefix: Optional[str],
+ ) -> None:
+ pass
+
+ def visit_array_type(
+ self,
+ name: str,
+ info: Optional[QAPISourceInfo],
+ ifcond: QAPISchemaIfCond,
+ element_type: QAPISchemaType,
+ ) -> None:
+ pass
+
+ def visit_command(
+ self,
+ name: str,
+ info: Optional[QAPISourceInfo],
+ ifcond: QAPISchemaIfCond,
+ features: List[QAPISchemaFeature],
+ arg_type: Optional[QAPISchemaObjectType],
+ ret_type: Optional[QAPISchemaType],
+ gen: bool,
+ success_response: bool,
+ boxed: bool,
+ allow_oob: bool,
+ allow_preconfig: bool,
+ coroutine: bool,
+ ) -> None:
+ pass
+
+ def visit_event(
+ self,
+ name: str,
+ info: Optional[QAPISourceInfo],
+ ifcond: QAPISchemaIfCond,
+ features: List[QAPISchemaFeature],
+ arg_type: Optional[QAPISchemaObjectType],
+ boxed: bool,
+ ) -> None:
+ pass
+
+ def write(self, outdir: str) -> None:
+ godir = "go"
+ targetpath = os.path.join(outdir, godir)
+ os.makedirs(targetpath, exist_ok=True)
+
+ # Content to be copied over
+ srcdir = os.path.dirname(os.path.realpath(__file__))
+ for filename in self.duplicate:
+ srcpath = os.path.join(srcdir, filename)
+ dstpath = os.path.join(targetpath, filename)
+ shutil.copyfile(srcpath, dstpath)
new file mode 100644
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2025 Red Hat, Inc.
+ * SPDX-License-Identifier: MIT-0
+ *
+ * Authors:
+ * Victor Toso <victortoso@redhat.com>
+ * Daniel P. Berrange <berrange@redhat.com>
+ */
+package qapi
+
+import (
+ "encoding/json"
+ "time"
+)
+
+/* Union of data for command, response, error, or event,
+ * since when receiving we don't know upfront which we
+ * must deserialize */
+type Message struct {
+ QMP *json.RawMessage `json:"QMP,omitempty"`
+ Execute string `json:"execute,omitempty"`
+ ExecOOB string `json:"exec-oob,omitempty"`
+ Event string `json:"event,omitempty"`
+ Error *json.RawMessage `json:"error,omitempty"`
+ Return *json.RawMessage `json:"return,omitempty"`
+ ID string `json:"id,omitempty"`
+ Timestamp *Timestamp `json:"timestamp,omitempty"`
+ Data *json.RawMessage `json:"data,omitempty"`
+ Arguments *json.RawMessage `json:"arguments,omitempty"`
+}
+
+type QAPIError struct {
+ Class string `json:"class"`
+ Description string `json:"desc"`
+}
+
+func (err *QAPIError) Error() string {
+ return err.Description
+}
+
+type Timestamp struct {
+ Seconds int `json:"seconds"`
+ MicroSeconds int `json:"microseconds"`
+}
+
+func (t *Timestamp) AsTime() time.Time {
+ return time.Unix(int64(t.Seconds), int64(t.MicroSeconds)*1000)
+}
@@ -16,6 +16,7 @@
from .error import QAPIError
from .events import gen_events
from .features import gen_features
+from .golang import golang
from .introspect import gen_introspect
from .schema import QAPISchema
from .types import gen_types
@@ -55,6 +56,7 @@ def generate(schema_file: str,
gen_commands(schema, output_dir, prefix, gen_tracing)
gen_events(schema, output_dir, prefix)
gen_introspect(schema, output_dir, prefix, unmask)
+ golang.gen_golang(schema, output_dir, prefix)
def main() -> int:
This first patch introduces protocol.go. It introduces the Message Go struct type that can unmarshall any QMP message. It does not handle deeper than 1st layer of the JSON object, that is, with: 1. { "execute": "query-machines", "arguments": { "compat-props": true } } 2. { "event": "BALLOON_CHANGE", "data": { "actual": 944766976 }, "timestamp": { "seconds": 1267020223, "microseconds": 435656 } } We will be able to know it is a query-machine command or a balloon-change event. Specific data type to handle arguments/data will be introduced further in the series. This patch also introduces the Visitor skeleton with a proper write() function to copy-over the protocol.go to the target destination. Note, you can execute any patch of this series with: python3 ./scripts/qapi-gen.py -o /tmp/out qapi/qapi-schema.json Signed-off-by: Victor Toso <victortoso@redhat.com> --- scripts/qapi/golang/__init__.py | 0 scripts/qapi/golang/golang.py | 135 ++++++++++++++++++++++++++++++++ scripts/qapi/golang/protocol.go | 48 ++++++++++++ scripts/qapi/main.py | 2 + 4 files changed, 185 insertions(+) create mode 100644 scripts/qapi/golang/__init__.py create mode 100644 scripts/qapi/golang/golang.py create mode 100644 scripts/qapi/golang/protocol.go