qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Victor Toso <victortoso@redhat.com>
To: qemu-devel@nongnu.org
Cc: "Markus Armbruster" <armbru@redhat.com>,
	"John Snow" <jsnow@redhat.com>,
	"Daniel P . Berrangé" <berrange@redhat.com>,
	"Andrea Bolognani" <abologna@redhat.com>
Subject: [PATCH v4 01/11] qapi: golang: first level unmarshalling type
Date: Fri, 14 Feb 2025 21:29:34 +0100	[thread overview]
Message-ID: <20250214202944.69897-2-victortoso@redhat.com> (raw)
In-Reply-To: <20250214202944.69897-1-victortoso@redhat.com>

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

diff --git a/scripts/qapi/golang/__init__.py b/scripts/qapi/golang/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/scripts/qapi/golang/golang.py b/scripts/qapi/golang/golang.py
new file mode 100644
index 0000000000..333741b47b
--- /dev/null
+++ b/scripts/qapi/golang/golang.py
@@ -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)
diff --git a/scripts/qapi/golang/protocol.go b/scripts/qapi/golang/protocol.go
new file mode 100644
index 0000000000..4ff8d2f3fb
--- /dev/null
+++ b/scripts/qapi/golang/protocol.go
@@ -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)
+}
diff --git a/scripts/qapi/main.py b/scripts/qapi/main.py
index 324081b9fc..af315c1ad1 100644
--- a/scripts/qapi/main.py
+++ b/scripts/qapi/main.py
@@ -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:
-- 
2.48.1



  reply	other threads:[~2025-02-14 20:31 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-02-14 20:29 [PATCH v4 00/11] Victor Toso
2025-02-14 20:29 ` Victor Toso [this message]
2025-02-14 20:29 ` [PATCH v4 02/11] qapi: golang: Generate enum type Victor Toso
2025-02-14 20:29 ` [PATCH v4 03/11] qapi: golang: Generate alternate types Victor Toso
2025-02-14 20:29 ` [PATCH v4 04/11] qapi: golang: Generate struct types Victor Toso
2025-02-14 20:29 ` [PATCH v4 05/11] qapi: golang: structs: Address nullable members Victor Toso
2025-02-14 20:29 ` [PATCH v4 06/11] qapi: golang: Generate union type Victor Toso
2025-02-14 20:29 ` [PATCH v4 07/11] qapi: golang: Generate event type Victor Toso
2025-02-14 20:29 ` [PATCH v4 08/11] qapi: golang: Generate Event interface Victor Toso
2025-02-14 20:29 ` [PATCH v4 09/11] qapi: golang: Generate command type Victor Toso
2025-02-14 20:29 ` [PATCH v4 10/11] qapi: golang: Generate Command sync/async interfaces Victor Toso
2025-02-14 20:29 ` [PATCH v4 11/11] docs: add notes on Golang code generator Victor Toso
2025-02-17 13:13 ` [PATCH v4 00/11] Daniel P. Berrangé
2025-02-20  8:06   ` Markus Armbruster
2025-02-17 14:58 ` Daniel P. Berrangé
2025-02-17 16:52   ` Victor Toso
2025-03-07 11:25     ` Victor Toso
2025-03-07 11:33       ` Daniel P. Berrangé

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250214202944.69897-2-victortoso@redhat.com \
    --to=victortoso@redhat.com \
    --cc=abologna@redhat.com \
    --cc=armbru@redhat.com \
    --cc=berrange@redhat.com \
    --cc=jsnow@redhat.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).