---
generator/GoLang.ml | 336 +++++++++++++++++-
.../libnbd/libnbd_200_connect_command_test.go | 35 ++
2 files changed, 367 insertions(+), 4 deletions(-)
diff --git a/generator/GoLang.ml b/generator/GoLang.ml
index 1b8f20b..e5fb97c 100644
--- a/generator/GoLang.ml
+++ b/generator/GoLang.ml
@@ -23,6 +23,304 @@ open Printf
open API
open Utils
+let go_name_of_arg = function
+ | Bool n -> n
+ | BytesIn (n, len) -> n
+ | BytesOut (n, len) -> n
+ | BytesPersistIn (n, len) -> n
+ | BytesPersistOut (n, len) -> n
+ | Closure { cbname } -> cbname
+ | Enum (n, _) -> n
+ | Fd n -> n
+ | Flags (n, _) -> n
+ | Int n -> n
+ | Int64 n -> n
+ | Path n -> n
+ | SockAddrAndLen (n, len) -> n
+ | String n -> n
+ | StringList n -> n
+ | UInt n -> n
+ | UInt32 n -> n
+ | UInt64 n -> n
+
+let go_arg_type_to_string = function
+ | Bool _ -> "bool"
+ | BytesIn _ -> "*string/*XXX*/"
+ | BytesPersistIn _ -> "*string/*XXX*/"
+ | BytesOut _ -> "*string/*XXX*/"
+ | BytesPersistOut _ -> "*string/*XXX*/"
+ | Closure { cbargs } -> "func (/*XXX*/)"
+ | Enum _ -> "uint32"
+ | Fd _ -> "int/*XXX*/"
+ | Flags _ -> "uint32"
+ | Int _ -> "int"
+ | Int64 _ -> "int64"
+ | Path _ -> "string"
+ | SockAddrAndLen _ -> "string"
+ | String _ -> "string"
+ | StringList _ -> "[]string"
+ | UInt _ -> "uint"
+ | UInt32 _ -> "uint32"
+ | UInt64 _ -> "uint64"
+
+let go_name_of_optarg = function
+ | OClosure { cbname } -> String.capitalize_ascii cbname
+ | OFlags (n, _) -> String.capitalize_ascii n
+
+(* For consistency every method will return (type, error) where
+ * type is defined by this function. However some functions
+ * never really have a type (RErr) and others never really have
+ * an error (RUInt).
+ *)
+let go_ret_type = function
+ | RBool -> "bool"
+ | RStaticString -> "*string"
+ | RErr -> "int" (* always 0 *)
+ | RFd -> "int/*XXX*/"
+ | RInt -> "int"
+ | RInt64 -> "int64"
+ | RCookie -> "int64"
+ | RString -> "*string"
+ | RUInt -> "uint"
+
+let go_ret_error = function
+ | RBool -> "false"
+ | RStaticString -> "nil"
+ | RErr -> "0"
+ | RFd -> "0/*XXX*/"
+ | RInt -> "0"
+ | RInt64 -> "0"
+ | RCookie -> "0"
+ | RString -> "nil"
+ | RUInt -> "0"
+
+let go_ret_c_errcode = function
+ | RBool -> Some "-1"
+ | RStaticString -> Some "nil"
+ | RErr -> Some "-1"
+ | RFd -> Some "-1/*XXX*/"
+ | RInt -> Some "-1"
+ | RInt64 -> Some "-1"
+ | RCookie -> Some "-1"
+ | RString -> Some "nil"
+ | RUInt -> None
+
+(* We need a wrapper around every function (except Close) to
+ * handle errors because cgo calls are sequence points and
+ * could result in us being rescheduled on another thread,
+ * but libnbd error handling means we must call nbd_get_error
+ * etc from the same thread.
+ *)
+let print_wrapper (name, { args; optargs; ret }) =
+ let ret_c_type = C.type_of_ret ret and errcode = C.errcode_of_ret ret in
+ pr "static %s\n" ret_c_type;
+ pr "_nbd_%s_wrapper (struct error *err,\n" name;
+ pr " ";
+ C.print_arg_list ~wrap:true ~handle:true ~parens:false args optargs;
+ pr ")\n";
+ pr "{\n";
+ pr " %s ret;\n" ret_c_type;
+ pr "\n";
+ pr " ret = nbd_%s " name;
+ C.print_arg_list ~wrap:true ~handle:true ~types:false args optargs;
+ pr ";\n";
+ (match errcode with
+ | None -> ()
+ | Some errcode ->
+ pr " if (ret == %s)\n" errcode;
+ pr " save_error (err);\n";
+ );
+ pr " return ret;\n";
+ pr "}\n";
+ pr "\n"
+
+let print_binding (name, { args; optargs; ret; shortdesc }) =
+ let uname = String.capitalize_ascii name in
+
+ (* Tedious method of passing optional arguments in golang. *)
+ if optargs <> [] then (
+ pr "/* Struct carrying optional arguments for %s. */\n" uname;
+ pr "type Optargs_%s struct {\n" uname;
+ List.iter (
+ fun optarg ->
+ let fname = go_name_of_optarg optarg in
+ pr " /* %s field is ignored unless %s_is_set == true. */\n"
+ fname fname;
+ pr " %s_is_set bool\n" fname;
+ pr " %s " fname;
+ (match optarg with
+ | OClosure { cbargs } -> pr "func (/*XXX*/)"
+ | OFlags _ -> pr "uint32"
+ );
+ pr "\n"
+ ) optargs;
+ pr "}\n";
+ pr "\n";
+ );
+
+ (* Define the golang function which calls the C wrapper. *)
+ pr "/* %s: %s */\n" uname shortdesc;
+ pr "func (h *Libnbd) %s (" uname;
+ let comma = ref false in
+ List.iter (
+ fun arg ->
+ if !comma then pr ", ";
+ comma := true;
+ pr "%s %s" (go_name_of_arg arg) (go_arg_type_to_string arg)
+ ) args;
+ if optargs <> [] then (
+ if !comma then pr ", ";
+ comma := true;
+ pr "optargs *Optargs_%s" uname
+ );
+ pr ") (%s, error)" (go_ret_type ret);
+ pr " {\n";
+ pr " if h.h == nil {\n";
+ pr " return %s, closed_handle_error (\"%s\")\n"
+ (go_ret_error ret) name;
+ pr " }\n";
+ pr "\n";
+ pr " c_err := C.struct_error{}\n";
+ List.iter (
+ function
+ | Bool n ->
+ pr " c_%s := C.bool (%s)\n" n n
+ | BytesIn (n, len) ->
+ pr " var c_%s unsafe.Pointer /* XXX */\n" n;
+ pr " var c_%s C.size_t /* XXX */\n" len;
+ | BytesOut (n, len) ->
+ pr " var c_%s unsafe.Pointer /* XXX */\n" n;
+ pr " var c_%s C.size_t /* XXX */\n" len;
+ | BytesPersistIn (n, len) ->
+ pr " var c_%s unsafe.Pointer /* XXX */\n" n;
+ pr " var c_%s C.size_t /* XXX */\n" len;
+ | BytesPersistOut (n, len) ->
+ pr " var c_%s unsafe.Pointer /* XXX */\n" n;
+ pr " var c_%s C.size_t /* XXX */\n" len;
+ | Closure { cbname } ->
+ pr " c_%s := C.nbd_%s_callback{} /* XXX */\n" cbname cbname
+ | Enum (n, _) ->
+ pr " c_%s := C.int (%s)\n" n n
+ | Fd n ->
+ pr " c_%s := C.int (%s) /* XXX */\n" n n
+ | Flags (n, _) ->
+ pr " c_%s := C.uint32_t (%s)\n" n n
+ | Int n ->
+ pr " c_%s := C.int (%s)\n" n n
+ | Int64 n ->
+ pr " c_%s := C.int64_t (%s)\n" n n
+ | Path n ->
+ pr " c_%s := C.CString (%s)\n" n n;
+ pr " defer C.free (unsafe.Pointer (c_%s))\n" n
+ | SockAddrAndLen (n, len) ->
+ pr " panic (\"SockAddrAndLen not supported\")\n";
+ pr " var c_%s *C.struct_sockaddr\n" n;
+ pr " var c_%s C.uint\n" len
+ | String n ->
+ pr " c_%s := C.CString (%s)\n" n n;
+ pr " defer C.free (unsafe.Pointer (c_%s))\n" n
+ | StringList n ->
+ pr " c_%s := arg_string_list (%s)\n" n n;
+ pr " defer free_string_list (c_%s)\n" n
+ | UInt n ->
+ pr " c_%s := C.uint (%s)\n" n n
+ | UInt32 n ->
+ pr " c_%s := C.uint32_t (%s)\n" n n
+ | UInt64 n ->
+ pr " c_%s := C.uint64_t (%s)\n" n n
+ ) args;
+ if optargs <> [] then (
+ List.iter (
+ function
+ | OClosure { cbname } ->
+ pr " c_%s := C.nbd_%s_callback{} /* XXX */\n" cbname cbname
+ | OFlags (n, _) ->
+ pr " var c_%s C.uint32_t\n" n
+ ) optargs;
+ pr " if optargs != nil {\n";
+ List.iter (
+ fun optarg ->
+ pr " if optargs.%s_is_set {\n" (go_name_of_optarg optarg);
+ (match optarg with
+ | OClosure { cbname } -> () (* XXX *)
+ | OFlags (n, _) ->
+ pr " c_%s = C.uint32_t (optargs.%s)\n"
+ n (go_name_of_optarg optarg);
+ );
+ pr " }\n";
+ ) optargs;
+ pr " }\n";
+ );
+ pr "\n";
+ pr " ret := C._nbd_%s_wrapper (&c_err, h.h" name;
+ List.iter (
+ function
+ | Bool n -> pr ", c_%s" n
+ | BytesIn (n, len) -> pr ", c_%s, c_%s" n len
+ | BytesOut (n, len) -> pr ", c_%s, c_%s" n len
+ | BytesPersistIn (n, len) -> pr ", c_%s, c_%s" n len
+ | BytesPersistOut (n, len) -> pr ", c_%s, c_%s" n len
+ | Closure { cbname } -> pr ", c_%s" cbname
+ | Enum (n, _) -> pr ", c_%s" n
+ | Fd n -> pr ", c_%s" n
+ | Flags (n, _) -> pr ", c_%s" n
+ | Int n -> pr ", c_%s" n
+ | Int64 n -> pr ", c_%s" n
+ | Path n -> pr ", c_%s" n
+ | SockAddrAndLen (n, len) -> pr ", c_%s, c_%s" n len
+ | String n -> pr ", c_%s" n
+ | StringList n -> pr ", c_%s" n
+ | UInt n -> pr ", c_%s" n
+ | UInt32 n -> pr ", c_%s" n
+ | UInt64 n -> pr ", c_%s" n
+ ) args;
+ List.iter (
+ function
+ | OClosure { cbname} -> pr ", c_%s" cbname
+ | OFlags (n, _) -> pr ", c_%s" n
+ ) optargs;
+ pr ")\n";
+ let errcode = go_ret_c_errcode ret in
+ (match errcode with
+ | None -> ()
+ | Some errcode ->
+ pr " if ret == %s {\n" errcode;
+ pr " err := get_error (\"%s\", c_err)\n" name;
+ pr " C.free_error (&c_err)\n";
+ pr " return %s, err\n" (go_ret_error ret);
+ pr " }\n";
+ );
+ (match ret with
+ | RBool ->
+ pr " r := int (ret)\n";
+ pr " if r != 0 { return true, nil } else { return false, nil }\n"
+ | RStaticString ->
+ pr " /* ret is statically allocated, do not free it. */\n";
+ pr " r := C.GoString (ret);\n";
+ pr " return &r, nil\n"
+ | RErr ->
+ pr " /* Note that the return value 0 has no meaning here.\n";
+ pr " * It is only returned for consistency.\n";
+ pr " */\n";
+ pr " return 0, nil\n"
+ | RFd ->
+ pr " return int/*XXX*/ (ret), nil\n"
+ | RInt ->
+ pr " return int (ret), nil\n"
+ | RInt64 ->
+ pr " return int64 (ret), nil\n"
+ | RCookie ->
+ pr " return int64 (ret), nil\n"
+ | RString ->
+ pr " r := C.GoString (ret)\n";
+ pr " C.free (unsafe.Pointer (ret))\n";
+ pr " return &r, nil\n"
+ | RUInt ->
+ pr " return uint (ret), nil\n"
+ );
+ pr "}\n";
+ pr "\n"
+
let generate_golang_libnbd_go () =
generate_header CStyle;
@@ -62,14 +360,20 @@ free_error (struct error *err)
static struct nbd_handle *
_nbd_create_wrapper (struct error *err)
{
- struct nbd_handle *r;
+ struct nbd_handle *ret;
- r = nbd_create ();
- if (r == NULL)
+ ret = nbd_create ();
+ if (ret == NULL)
save_error (err);
- return r;
+ return ret;
}
+";
+
+ (* Wrappers. *)
+ List.iter print_wrapper handle_calls;
+
+ pr "\
// There must be no blank line between end comment and import!
//
https://github.com/golang/go/issues/9733
*/
@@ -79,6 +383,7 @@ import (
\"fmt\"
\"runtime\"
\"syscall\"
+ \"unsafe\"
)
/* Handle. */
@@ -147,4 +452,27 @@ func (h *Libnbd) Close () *LibnbdError {
return nil
}
+/* Functions for translating between NULL-terminated lists of
+ * C strings and golang []string.
+ */
+func arg_string_list (xs []string) **C.char {
+ r := make ([]*C.char, 1 + len (xs))
+ for i, x := range xs {
+ r[i] = C.CString (x)
+ }
+ r[len (xs)] = nil
+ return &r[0]
+}
+
+func free_string_list (argv **C.char) {
+ for *argv != nil {
+ //C.free (*argv)
+ argv = (**C.char) (unsafe.Pointer (uintptr (unsafe.Pointer (argv)) +
+ unsafe.Sizeof (*argv)))
+ }
+}
+
";
+
+ (* Bindings. *)
+ List.iter print_binding handle_calls;
diff --git
a/golang/src/libguestfs.org/libnbd/libnbd_200_connect_command_test.go
b/golang/src/libguestfs.org/libnbd/libnbd_200_connect_command_test.go
new file mode 100644
index 0000000..9faf244
--- /dev/null
+++
b/golang/src/libguestfs.org/libnbd/libnbd_200_connect_command_test.go
@@ -0,0 +1,35 @@
+/* libnbd golang tests
+ * Copyright (C) 2013-2020 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+package libnbd
+
+import "testing"
+
+func Test200ConnectCommand (t *testing.T) {
+ h, err := Create ()
+ if err != nil {
+ t.Errorf ("could not create handle: %s", err)
+ }
+ _, err = h.Connect_command ([]string{
+ "nbdkit", "-s", "--exit-with-parent",
"-v", "null",
+ })
+ if err != nil {
+ t.Errorf ("could not connect: %s", err)
+ }
+ h.Close ()
+}
--
2.25.0