Richard W.M. Jones
2020-Mar-17 21:20 UTC
[Libguestfs] [PATCH libnbd v2 0/3] Unfinished golang bindings.
These bindings get as far as running very simple connections. However there are many missing parts still: * No callbacks. * No functions which handle buffers (pread/pwrite!) This is posted just for general early interest, not even for review. Rich.
Richard W.M. Jones
2020-Mar-17 21:20 UTC
[Libguestfs] [PATCH libnbd v2 1/3] generator: Modify C.print_* with optional parens flag.
Controls whether or not we print the parentheses around the function parameters. --- generator/C.ml | 26 ++++++++++++++------------ generator/C.mli | 5 +++-- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/generator/C.ml b/generator/C.ml index e79ac42..568e3f1 100644 --- a/generator/C.ml +++ b/generator/C.ml @@ -96,16 +96,17 @@ let rec name_of_arg = function | UInt32 n -> [n] | UInt64 n -> [n] -let rec print_arg_list ?(wrap = false) ?maxcol ?handle ?types args optargs - pr "("; +let rec print_arg_list ?(wrap = false) ?maxcol ?handle ?types ?(parens = true) + args optargs + if parens then pr "("; if wrap then pr_wrap ?maxcol ',' - (fun () -> print_arg_list_no_parens ?handle ?types args optargs) + (fun () -> print_arg_list' ?handle ?types args optargs) else - print_arg_list_no_parens ?handle ?types args optargs; - pr ")" + print_arg_list' ?handle ?types args optargs; + if parens then pr ")" -and print_arg_list_no_parens ?(handle = false) ?(types = true) args optargs +and print_arg_list' ?(handle = false) ?(types = true) args optargs let comma = ref false in if handle then ( comma := true; @@ -191,16 +192,17 @@ let print_extern ?wrap name args optargs ret print_call ?wrap name args optargs ret; pr ";\n" -let rec print_cbarg_list ?(wrap = false) ?maxcol ?types cbargs - pr "("; +let rec print_cbarg_list ?(wrap = false) ?maxcol ?types ?(parens = true) + cbargs + if parens then pr "("; if wrap then pr_wrap ?maxcol ',' - (fun () -> print_cbarg_list_no_parens ?types cbargs) + (fun () -> print_cbarg_list' ?types cbargs) else - print_cbarg_list_no_parens ?types cbargs; - pr ")" + print_cbarg_list' ?types cbargs; + if parens then pr ")" -and print_cbarg_list_no_parens ?(types = true) cbargs +and print_cbarg_list' ?(types = true) cbargs if types then pr "void *"; pr "user_data"; diff --git a/generator/C.mli b/generator/C.mli index 7748bc7..c8268c1 100644 --- a/generator/C.mli +++ b/generator/C.mli @@ -25,9 +25,10 @@ val generate_docs_api_links_pod : unit -> unit val generate_docs_api_flag_links_pod : unit -> unit val generate_docs_nbd_pod : string -> API.call -> unit -> unit val print_arg_list : ?wrap:bool -> ?maxcol:int -> - ?handle:bool -> ?types:bool -> + ?handle:bool -> ?types:bool -> ?parens:bool -> API.arg list -> API.optarg list -> unit -val print_cbarg_list : ?wrap:bool -> ?maxcol:int -> ?types:bool -> +val print_cbarg_list : ?wrap:bool -> ?maxcol:int -> + ?types:bool -> ?parens:bool -> API.cbarg list -> unit val errcode_of_ret : API.ret -> string option val type_of_ret : API.ret -> string -- 2.25.0
Richard W.M. Jones
2020-Mar-17 21:20 UTC
[Libguestfs] [PATCH libnbd v2 2/3] Add outline framework for Go language bindings (golang).
This simply compiles, passes tests, but is only able open a handle. This commit does not contain the full bindings. --- Makefile.am | 2 + configure.ac | 32 ++++ generator/GoLang.ml | 150 ++++++++++++++++++ generator/GoLang.mli | 19 +++ generator/Makefile.am | 2 + generator/generator.ml | 3 + golang/Makefile.am | 58 +++++++ golang/config-test.go | 34 ++++ golang/examples/LICENSE-FOR-EXAMPLES | 38 +++++ golang/examples/Makefile.am | 21 +++ golang/run-tests.sh | 24 +++ golang/src/libguestfs.org/libnbd/.gitignore | 1 + .../libnbd/libnbd_010_load_test.go | 25 +++ .../libnbd/libnbd_100_handle_test.go | 29 ++++ run.in | 17 ++ 15 files changed, 455 insertions(+) diff --git a/Makefile.am b/Makefile.am index bf2db68..a9f13ca 100644 --- a/Makefile.am +++ b/Makefile.am @@ -43,6 +43,8 @@ SUBDIRS = \ ocaml \ ocaml/examples \ ocaml/tests \ + golang \ + golang/examples \ interop \ fuzzing \ bash \ diff --git a/configure.ac b/configure.ac index 9fd284b..36617fc 100644 --- a/configure.ac +++ b/configure.ac @@ -403,6 +403,34 @@ AS_IF([test "x$enable_python" != "xno"],[ AM_CONDITIONAL([HAVE_PYTHON], [test "x$PYTHON" != "xno" && test "x$have_python_module" = "x1" ]) +dnl Golang. +AC_ARG_ENABLE([golang], + AS_HELP_STRING([--disable-golang], [disable Go language bindings]), + [], + [enable_golang=yes]) +AS_IF([test "x$enable_golang" != "xno"],[ + AC_CHECK_PROG([GOLANG],[go],[go],[no]) + AS_IF([test "x$GOLANG" != "xno"],[ + AC_MSG_CHECKING([if $GOLANG is usable]) + AS_IF([$GOLANG run $srcdir/golang/config-test.go 2>&AS_MESSAGE_LOG_FD],[ + AC_MSG_RESULT([yes]) + + # Substitute some golang environment. + GOOS=`$GOLANG env GOOS` + GOARCH=`$GOLANG env GOARCH` + GOROOT=`$GOLANG env GOROOT` + AC_SUBST([GOOS]) + AC_SUBST([GOARCH]) + AC_SUBST([GOROOT]) + ],[ + AC_MSG_RESULT([no]) + AC_MSG_WARN([golang ($GOLANG) is installed but not usable]) + GOLANG=no + ]) + ]) +],[GOLANG=no]) +AM_CONDITIONAL([HAVE_GOLANG],[test "x$GOLANG" != "xno"]) + dnl Produce output files. AC_CONFIG_HEADERS([config.h]) @@ -423,6 +451,8 @@ AC_CONFIG_FILES([Makefile fuse/Makefile fuzzing/Makefile generator/Makefile + golang/Makefile + golang/examples/Makefile include/Makefile interop/Makefile lib/Makefile @@ -472,6 +502,8 @@ feature "Bash tab completion .................... " \ echo echo "Language bindings:" echo +feature "Go ..................................... " \ + test "x$HAVE_GOLANG_TRUE" = "x" feature "OCaml .................................. " \ test "x$HAVE_OCAML_TRUE" = "x" feature "Python ................................. " \ diff --git a/generator/GoLang.ml b/generator/GoLang.ml new file mode 100644 index 0000000..1b8f20b --- /dev/null +++ b/generator/GoLang.ml @@ -0,0 +1,150 @@ +(* nbd client library in userspace: generator + * Copyright (C) 2013-2020 Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + *) + +(* Go language bindings. *) + +open Printf + +open API +open Utils + +let generate_golang_libnbd_go () + generate_header CStyle; + + pr "\ +package libnbd + +/* +#cgo CFLAGS: +#cgo LDFLAGS: -lnbd + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include \"libnbd.h\" + +struct error { + char *error; + int errnum; +}; + +static void +save_error (struct error *err) +{ + err->error = strdup (nbd_get_error ()); + err->errnum = nbd_get_errno (); +} + +static void +free_error (struct error *err) +{ + free (err->error); +} + +static struct nbd_handle * +_nbd_create_wrapper (struct error *err) +{ + struct nbd_handle *r; + + r = nbd_create (); + if (r == NULL) + save_error (err); + return r; +} + +// There must be no blank line between end comment and import! +// https://github.com/golang/go/issues/9733 +*/ +import \"C\" + +import ( + \"fmt\" + \"runtime\" + \"syscall\" +) + +/* Handle. */ +type Libnbd struct { + h *C.struct_nbd_handle +} + +/* Convert handle to string (just for debugging). */ +func (h *Libnbd) String () string { + return \"&Libnbd{}\" +} + +/* All functions (except Close) return ([result,] LibnbdError). */ +type LibnbdError struct { + Op string // operation which failed + Errmsg string // string (nbd_get_error) + Errno syscall.Errno // errno (nbd_get_errno) +} + +func (e *LibnbdError) String() string { + if e.Errno != 0 { + return fmt.Sprintf (\"%%s: %%s\", e.Op, e.Errmsg); + } else { + return fmt.Sprintf (\"%%s: %%s: %%s\", e.Op, e.Errmsg, e.Errno); + } +} + +/* Implement the error interface */ +func (e *LibnbdError) Error() string { + return e.String() +} + +func get_error (op string, c_err C.struct_error) *LibnbdError { + errmsg := C.GoString (c_err.error) + errno := syscall.Errno (c_err.errnum) + return &LibnbdError{ Op : op, Errmsg : errmsg, Errno : errno } +} + +func closed_handle_error (op string) *LibnbdError { + return &LibnbdError{ Op : op, Errmsg : \"handle is closed\", + Errno : syscall.Errno (0) } +} + +/* Create a new handle. */ +func Create () (*Libnbd, error) { + c_err := C.struct_error{} + c_h := C._nbd_create_wrapper (&c_err) + if c_h == nil { + err := get_error (\"create\", c_err) + C.free_error (&c_err) + return nil, err + } + h := &Libnbd{h : c_h} + // Finalizers aren't guaranteed to run, but try having one anyway ... + runtime.SetFinalizer (h, (*Libnbd).Close) + return h, nil +} + +/* Close the handle. */ +func (h *Libnbd) Close () *LibnbdError { + if h.h == nil { + return closed_handle_error (\"close\") + } + C.nbd_close (h.h) + h.h = nil + return nil +} + +"; diff --git a/generator/GoLang.mli b/generator/GoLang.mli new file mode 100644 index 0000000..b8ec1b5 --- /dev/null +++ b/generator/GoLang.mli @@ -0,0 +1,19 @@ +(* nbd client library in userspace: generator + * Copyright (C) 2013-2020 Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + *) + +val generate_golang_libnbd_go : unit -> unit diff --git a/generator/Makefile.am b/generator/Makefile.am index e499ca8..0389d70 100644 --- a/generator/Makefile.am +++ b/generator/Makefile.am @@ -57,6 +57,8 @@ sources = \ Python.ml \ OCaml.mli \ OCaml.ml \ + GoLang.mli \ + GoLang.ml \ generator.ml \ $(NULL) diff --git a/generator/generator.ml b/generator/generator.ml index 1c97492..817c032 100755 --- a/generator/generator.ml +++ b/generator/generator.ml @@ -55,3 +55,6 @@ let () output_to "ocaml/NBD.mli" OCaml.generate_ocaml_nbd_mli; output_to "ocaml/NBD.ml" OCaml.generate_ocaml_nbd_ml; output_to "ocaml/nbd-c.c" OCaml.generate_ocaml_nbd_c; + + output_to "golang/src/libguestfs.org/libnbd/libnbd.go" + GoLang.generate_golang_libnbd_go; diff --git a/golang/Makefile.am b/golang/Makefile.am new file mode 100644 index 0000000..47234ff --- /dev/null +++ b/golang/Makefile.am @@ -0,0 +1,58 @@ +# nbd client library in userspace +# Copyright (C) 2013-2020 Red Hat Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +include $(top_srcdir)/subdir-rules.mk + +# http://golang.org/doc/code.html#Organization +pkg = libguestfs.org/libnbd + +source_files = \ + src/$(pkg)/libnbd.go \ + src/$(pkg)/libnbd_*_test.go + +generator_built = \ + $(source_files) + +EXTRA_DIST = \ + src/$(pkg)/.gitignore \ + $(generator_built) \ + config-test.go \ + run-tests.sh + +if HAVE_GOLANG + +golangpkgdir = $(GOROOT)/pkg/$(GOOS)_$(GOARCH)/$(pkg) +golangsrcdir = $(GOROOT)/src/pkg/$(pkg) + +golangpkg_DATA = \ + pkg/$(GOOS)_$(GOARCH)/$(pkg).a + +pkg/$(GOOS)_$(GOARCH)/$(pkg).a: src/$(pkg)/libnbd.go + $(top_builddir)/run $(GOLANG) install $(pkg) + +golangsrc_DATA = $(source_files) + +TESTS_ENVIRONMENT = pkg=$(pkg) LIBNBD_DEBUG=1 +LOG_COMPILER = $(top_builddir)/run +TESTS = run-tests.sh + +endif + +CLEANFILES += src/$(pkg)/*~ + +clean-local: + rm -rf pkg diff --git a/golang/config-test.go b/golang/config-test.go new file mode 100644 index 0000000..e104c71 --- /dev/null +++ b/golang/config-test.go @@ -0,0 +1,34 @@ +/* libnbd Go configuration test + * 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 + */ + +/* This is called from ./configure to check that golang works + * and is above the minimum required version. + */ + +package main + +func main() { + /* XXX Check for minimum runtime.Version() >= "go1.1.1" + * Unfortunately go version numbers are not easy to parse. + * They have the 3 formats "goX.Y.Z", "release.rN" or + * "weekly.YYYY-MM-DD". The latter two formats are mostly + * useless, and the first one is hard to parse. See also + * cmpGoVersion in + * http://web.archive.org/web/20130402235148/http://golang.org/src/cmd/go/get.go?m=text + */ +} diff --git a/golang/examples/LICENSE-FOR-EXAMPLES b/golang/examples/LICENSE-FOR-EXAMPLES new file mode 100644 index 0000000..4986466 --- /dev/null +++ b/golang/examples/LICENSE-FOR-EXAMPLES @@ -0,0 +1,38 @@ +The files in the golang/examples/ directory are licensed under this +very permissive BSD license. This means that you can copy, use, adapt +and modify them without any significant restrictions. You can also +combine them with proprietary code or include them in code that is +distributed under other open source licenses. + +---------------------------------------------------------------------- + +libnbd examples +Copyright (C) 2013-2020 Red Hat Inc. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +* Neither the name of Red Hat nor the names of its contributors may be +used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. diff --git a/golang/examples/Makefile.am b/golang/examples/Makefile.am new file mode 100644 index 0000000..90498ab --- /dev/null +++ b/golang/examples/Makefile.am @@ -0,0 +1,21 @@ +# nbd client library in userspace +# Copyright (C) 2013-2020 Red Hat Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +include $(top_srcdir)/subdir-rules.mk + +EXTRA_DIST = \ + LICENSE-FOR-EXAMPLES diff --git a/golang/run-tests.sh b/golang/run-tests.sh new file mode 100755 index 0000000..a155819 --- /dev/null +++ b/golang/run-tests.sh @@ -0,0 +1,24 @@ +#!/bin/sh - +# nbd client library in userspace +# Copyright (C) 2013-2020 Red Hat Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +set -e + +# The -count=1 parameter is the "idiomatic way to bypass test caching". +# https://golang.org/doc/go1.10#test +# The -v option enables verbose output. +$GOLANG test -count=1 -v $pkg diff --git a/golang/src/libguestfs.org/libnbd/.gitignore b/golang/src/libguestfs.org/libnbd/.gitignore new file mode 100644 index 0000000..a2ce7eb --- /dev/null +++ b/golang/src/libguestfs.org/libnbd/.gitignore @@ -0,0 +1 @@ +/libnbd.go diff --git a/golang/src/libguestfs.org/libnbd/libnbd_010_load_test.go b/golang/src/libguestfs.org/libnbd/libnbd_010_load_test.go new file mode 100644 index 0000000..24cfe66 --- /dev/null +++ b/golang/src/libguestfs.org/libnbd/libnbd_010_load_test.go @@ -0,0 +1,25 @@ +/* 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 Test010Load (t *testing.T) { + /* Nothing - just test that the library can be linked to. */ +} diff --git a/golang/src/libguestfs.org/libnbd/libnbd_100_handle_test.go b/golang/src/libguestfs.org/libnbd/libnbd_100_handle_test.go new file mode 100644 index 0000000..a3d933d --- /dev/null +++ b/golang/src/libguestfs.org/libnbd/libnbd_100_handle_test.go @@ -0,0 +1,29 @@ +/* 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 Test100Handle (t *testing.T) { + h, err := Create () + if err != nil { + t.Errorf ("could not create handle: %s", err) + } + h.Close () +} diff --git a/run.in b/run.in index 0411c85..9ffece5 100755 --- a/run.in +++ b/run.in @@ -78,6 +78,23 @@ export PYTHONPATH prepend CAML_LD_LIBRARY_PATH "$b/ocaml" export CAML_LD_LIBRARY_PATH +# For golang. +export GOLANG="@GOLANG@" +prepend GOPATH "$b/golang" +export GOPATH +if [ -z "$CGO_CFLAGS" ]; then + CGO_CFLAGS="-I$s/include -I$b" +else + CGO_CFLAGS="$CGO_CFLAGS -I$s/include -I$b" +fi +export CGO_CFLAGS +if [ -z "$CGO_LDFLAGS" ]; then + CGO_LDFLAGS="-L$b/lib/.libs" +else + CGO_LDFLAGS="$CGO_LDFLAGS -L$b/lib/.libs" +fi +export CGO_LDFLAGS + # This is a cheap way to find some use-after-free and uninitialized # read problems when using glibc. export MALLOC_CHECK_=1 -- 2.25.0
Richard W.M. Jones
2020-Mar-17 21:20 UTC
[Libguestfs] [PATCH libnbd v2 3/3] golang: Add straightforward bindings, without callbacks.
--- 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
Daniel P. Berrangé
2020-Mar-18 09:32 UTC
Re: [Libguestfs] [PATCH libnbd v2 2/3] Add outline framework for Go language bindings (golang).
On Tue, Mar 17, 2020 at 09:20:12PM +0000, Richard W.M. Jones wrote:> + > +/* > +#cgo CFLAGS: > +#cgo LDFLAGS: -lnbdI assume you support pkg-config for libnbd, and thus you should just do #cgo pkg-config: libnbd> + > +#include <config.h> > + > +#include <stdio.h> > +#include <stdlib.h> > +#include <string.h> > + > +#include \"libnbd.h\" > + > +struct error { > + char *error; > + int errnum; > +}; > + > +static void > +save_error (struct error *err) > +{ > + err->error = strdup (nbd_get_error ()); > + err->errnum = nbd_get_errno (); > +} > + > +static void > +free_error (struct error *err) > +{ > + free (err->error); > +} > + > +static struct nbd_handle * > +_nbd_create_wrapper (struct error *err) > +{ > + struct nbd_handle *r; > + > + r = nbd_create (); > + if (r == NULL) > + save_error (err); > + return r; > +} > + > +// There must be no blank line between end comment and import! > +// https://github.com/golang/go/issues/9733 > +*/ > +import \"C\" > + > +import ( > + \"fmt\" > + \"runtime\" > + \"syscall\" > +) > + > +/* Handle. */ > +type Libnbd struct { > + h *C.struct_nbd_handle > +} > + > +/* Convert handle to string (just for debugging). */ > +func (h *Libnbd) String () string { > + return \"&Libnbd{}\" > +} > + > +/* All functions (except Close) return ([result,] LibnbdError). */ > +type LibnbdError struct { > + Op string // operation which failed > + Errmsg string // string (nbd_get_error) > + Errno syscall.Errno // errno (nbd_get_errno) > +} > + > +func (e *LibnbdError) String() string { > + if e.Errno != 0 { > + return fmt.Sprintf (\"%%s: %%s\", e.Op, e.Errmsg); > + } else { > + return fmt.Sprintf (\"%%s: %%s: %%s\", e.Op, e.Errmsg, e.Errno); > + } > +} > + > +/* Implement the error interface */ > +func (e *LibnbdError) Error() string { > + return e.String() > +} > + > +func get_error (op string, c_err C.struct_error) *LibnbdError { > + errmsg := C.GoString (c_err.error) > + errno := syscall.Errno (c_err.errnum) > + return &LibnbdError{ Op : op, Errmsg : errmsg, Errno : errno } > +} > + > +func closed_handle_error (op string) *LibnbdError { > + return &LibnbdError{ Op : op, Errmsg : \"handle is closed\", > + Errno : syscall.Errno (0) } > +} > + > +/* Create a new handle. */ > +func Create () (*Libnbd, error) { > + c_err := C.struct_error{} > + c_h := C._nbd_create_wrapper (&c_err) > + if c_h == nil { > + err := get_error (\"create\", c_err) > + C.free_error (&c_err) > + return nil, err > + } > + h := &Libnbd{h : c_h} > + // Finalizers aren't guaranteed to run, but try having one anyway ... > + runtime.SetFinalizer (h, (*Libnbd).Close) > + return h, nil > +} > + > +/* Close the handle. */ > +func (h *Libnbd) Close () *LibnbdError { > + if h.h == nil { > + return closed_handle_error (\"close\") > + } > + C.nbd_close (h.h) > + h.h = nil > + return nil > +} > + > +"; > diff --git a/generator/GoLang.mli b/generator/GoLang.mli > new file mode 100644 > index 0000000..b8ec1b5 > --- /dev/null > +++ b/generator/GoLang.mli > @@ -0,0 +1,19 @@ > +(* nbd client library in userspace: generator > + * Copyright (C) 2013-2020 Red Hat Inc. > + * > + * This library is free software; you can redistribute it and/or > + * modify it under the terms of the GNU Lesser General Public > + * License as published by the Free Software Foundation; either > + * version 2 of the License, or (at your option) any later version. > + * > + * This library 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 > + * Lesser General Public License for more details. > + * > + * You should have received a copy of the GNU Lesser General Public > + * License along with this library; if not, write to the Free Software > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA > + *) > + > +val generate_golang_libnbd_go : unit -> unit > diff --git a/generator/Makefile.am b/generator/Makefile.am > index e499ca8..0389d70 100644 > --- a/generator/Makefile.am > +++ b/generator/Makefile.am > @@ -57,6 +57,8 @@ sources = \ > Python.ml \ > OCaml.mli \ > OCaml.ml \ > + GoLang.mli \ > + GoLang.ml \ > generator.ml \ > $(NULL) > > diff --git a/generator/generator.ml b/generator/generator.ml > index 1c97492..817c032 100755 > --- a/generator/generator.ml > +++ b/generator/generator.ml > @@ -55,3 +55,6 @@ let () > output_to "ocaml/NBD.mli" OCaml.generate_ocaml_nbd_mli; > output_to "ocaml/NBD.ml" OCaml.generate_ocaml_nbd_ml; > output_to "ocaml/nbd-c.c" OCaml.generate_ocaml_nbd_c; > + > + output_to "golang/src/libguestfs.org/libnbd/libnbd.go" > + GoLang.generate_golang_libnbd_go; > diff --git a/golang/Makefile.am b/golang/Makefile.am > new file mode 100644 > index 0000000..47234ff > --- /dev/null > +++ b/golang/Makefile.am > @@ -0,0 +1,58 @@ > +# nbd client library in userspace > +# Copyright (C) 2013-2020 Red Hat Inc. > +# > +# This library is free software; you can redistribute it and/or > +# modify it under the terms of the GNU Lesser General Public > +# License as published by the Free Software Foundation; either > +# version 2 of the License, or (at your option) any later version. > +# > +# This library 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 > +# Lesser General Public License for more details. > +# > +# You should have received a copy of the GNU Lesser General Public > +# License along with this library; if not, write to the Free Software > +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA > + > +include $(top_srcdir)/subdir-rules.mk > + > +# http://golang.org/doc/code.html#Organization > +pkg = libguestfs.org/libnbd > + > +source_files = \ > + src/$(pkg)/libnbd.go \ > + src/$(pkg)/libnbd_*_test.go > + > +generator_built = \ > + $(source_files) > + > +EXTRA_DIST = \ > + src/$(pkg)/.gitignore \ > + $(generator_built) \ > + config-test.go \ > + run-tests.sh > + > +if HAVE_GOLANG > + > +golangpkgdir = $(GOROOT)/pkg/$(GOOS)_$(GOARCH)/$(pkg) > +golangsrcdir = $(GOROOT)/src/pkg/$(pkg) > + > +golangpkg_DATA = \ > + pkg/$(GOOS)_$(GOARCH)/$(pkg).a > + > +pkg/$(GOOS)_$(GOARCH)/$(pkg).a: src/$(pkg)/libnbd.go > + $(top_builddir)/run $(GOLANG) install $(pkg) > + > +golangsrc_DATA = $(source_files) > + > +TESTS_ENVIRONMENT = pkg=$(pkg) LIBNBD_DEBUG=1 > +LOG_COMPILER = $(top_builddir)/run > +TESTS = run-tests.sh > + > +endif > + > +CLEANFILES += src/$(pkg)/*~ > + > +clean-local: > + rm -rf pkg > diff --git a/golang/config-test.go b/golang/config-test.go > new file mode 100644 > index 0000000..e104c71 > --- /dev/null > +++ b/golang/config-test.go > @@ -0,0 +1,34 @@ > +/* libnbd Go configuration test > + * 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 > + */ > + > +/* This is called from ./configure to check that golang works > + * and is above the minimum required version. > + */ > + > +package main > + > +func main() { > + /* XXX Check for minimum runtime.Version() >= "go1.1.1" > + * Unfortunately go version numbers are not easy to parse. > + * They have the 3 formats "goX.Y.Z", "release.rN" or > + * "weekly.YYYY-MM-DD". The latter two formats are mostly > + * useless, and the first one is hard to parse. See also > + * cmpGoVersion in > + * http://web.archive.org/web/20130402235148/http://golang.org/src/cmd/go/get.go?m=text > + */I believe you can just achieve the version check using a build tag comment at the top of the source file eg to mandate go 1.10 or later: // +build go1.10 See build constraints here: https://golang.org/pkg/go/build/ 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 :|
Daniel P. Berrangé
2020-Mar-18 09:37 UTC
Re: [Libguestfs] [PATCH libnbd v2 3/3] golang: Add straightforward bindings, without callbacks.
On Tue, Mar 17, 2020 at 09:20:13PM +0000, Richard W.M. Jones wrote:> + | Fd _ -> "int/*XXX*/"snip.> + | RFd -> "int/*XXX*/"snip.> + | RFd -> "0/*XXX*/"snip.> + | RFd -> Some "-1/*XXX*/"snip. For passing file descriptors in or out of Go APIs you'll want to use the os.File object. To pass it from Go to C use a param 'file os.File' and call 'C.int(file.Fd())' to get the POSIX file descriptor to pass to C. libvirt-go OpenGraphics API gives an illustration of this. For the reverse direction from C to Go use os.NewFile(uintptr(fd), "name") where 'name' is any sensibiel identifying short name. libvirt-go OpenGraphicsFD gives an illustration of this direction. 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 :|
Reasonably Related Threads
- [PATCH libnbd] Add outline framework for Go language bindings (golang).
- [PATCH libnbd v2 0/3] Unfinished golang bindings.
- [PATCH libnbd v3] Add Go language bindings (golang) (RHBZ#1814538).
- [PATCH 2/2] golang: Don't run launch test if appliance has not been built.
- [PATCH libnbd v4] Add Go language bindings (golang) (RHBZ#1814538).