diff --git a/contrib/golang/COPYING b/contrib/golang/COPYING new file mode 100644 index 00000000..aa93b4da --- /dev/null +++ b/contrib/golang/COPYING @@ -0,0 +1,17 @@ +Portions of this code are derived from: + +https://github.com/dradtke/gotk3 + +Copyright (c) 2013 Conformal Systems LLC. + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/contrib/golang/README.md b/contrib/golang/README.md new file mode 100644 index 00000000..60a4856d --- /dev/null +++ b/contrib/golang/README.md @@ -0,0 +1,2 @@ +This file contains demonstration FFI bindings for using `-lostree-1` +and `-larchive` from golang. diff --git a/contrib/golang/glibobject.go b/contrib/golang/glibobject.go new file mode 100644 index 00000000..585ccd70 --- /dev/null +++ b/contrib/golang/glibobject.go @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2013 Conformal Systems + * + * This file originated from: http://opensource.conformal.com/ + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package ostree + +// #cgo pkg-config: glib-2.0 gobject-2.0 +// #include +// #include +// #include +// #include "glibobject.go.h" +// #include +import "C" +import ( + "unsafe" + "runtime" + "fmt" + "errors" +) + +func GBool(b bool) C.gboolean { + if b { + return C.gboolean(1) + } + return C.gboolean(0) +} + +func GoBool(b C.gboolean) bool { + if b != 0 { + return true + } + return false +} + +type GError struct { + ptr unsafe.Pointer +} + +func NewGError() GError { + return GError{nil} +} + +func (e *GError) Native() *C.GError { + if e == nil { + return nil + } + return (*C.GError)(e.ptr) +} + +func ConvertGError(e *C.GError) error { + defer C.g_error_free(e) + return errors.New(C.GoString((*C.char)(C._g_error_get_message(e)))) +} + +type GType uint + +func (t GType) Name() string { + return C.GoString((*C.char)(C.g_type_name(C.GType(t)))) +} + +type GVariant struct { + ptr unsafe.Pointer +} + +func GVariantNew(p unsafe.Pointer) *GVariant { + o := &GVariant{p} + runtime.SetFinalizer(o, (*GVariant).Unref) + return o; +} + +func GVariantNewSink(p unsafe.Pointer) *GVariant { + o := &GVariant{p} + runtime.SetFinalizer(o, (*GVariant).Unref) + o.RefSink() + return o; +} + +func (v *GVariant) native() *C.GVariant { + return (*C.GVariant)(v.ptr); +} + +func (v *GVariant) Ref() { + C.g_variant_ref(v.native()) +} + +func (v *GVariant) Unref() { + C.g_variant_unref(v.native()) +} + +func (v *GVariant) RefSink() { + C.g_variant_ref_sink(v.native()) +} + +func (v *GVariant) TypeString() string { + cs := (*C.char)(C.g_variant_get_type_string(v.native())) + return C.GoString(cs) +} + +func (v *GVariant) GetChildValue(i int) *GVariant { + cchild := C.g_variant_get_child_value(v.native(), C.gsize(i)) + return GVariantNew(unsafe.Pointer(cchild)); +} + +func (v *GVariant) LookupString(key string) (string, error) { + ckey := C.CString(key) + defer C.free(unsafe.Pointer(ckey)) + // TODO: Find a way to have constant C strings in golang + cstr := C._g_variant_lookup_string(v.native(), ckey) + if cstr == nil { + return "", fmt.Errorf("No such key: %s", key) + } + return C.GoString(cstr), nil +} + +/* + * GObject + */ + +// IObject is an interface type implemented by Object and all types which embed +// an Object. It is meant to be used as a type for function arguments which +// require GObjects or any subclasses thereof. +type IObject interface { + toGObject() *C.GObject + ToObject() *GObject +} + +// Object is a representation of GLib's GObject. +type GObject struct { + ptr unsafe.Pointer +} + +func GObjectNew(p unsafe.Pointer) *GObject { + o := &GObject{p} + runtime.SetFinalizer(o, (*GObject).Unref) + return o; +} + +func (v *GObject) Ptr() unsafe.Pointer { + return v.ptr +} + +func (v *GObject) Native() *C.GObject { + if v == nil || v.ptr == nil { + return nil + } + return (*C.GObject)(v.ptr) +} + +func (v *GObject) toGObject() *C.GObject { + if v == nil { + return nil + } + return v.Native() +} + +func (v *GObject) Ref() { + C.g_object_ref(C.gpointer(v.ptr)) +} + +func (v *GObject) Unref() { + C.g_object_unref(C.gpointer(v.ptr)) +} + +func (v *GObject) RefSink() { + C.g_object_ref_sink(C.gpointer(v.ptr)) +} + +func (v *GObject) IsFloating() bool { + c := C.g_object_is_floating(C.gpointer(v.ptr)) + return GoBool(c) +} + +func (v *GObject) ForceFloating() { + C.g_object_force_floating((*C.GObject)(v.ptr)) +} + +// GIO types + +type GCancellable struct { + *GObject +} + +func (self *GCancellable) native() *C.GCancellable { + return (*C.GCancellable)(self.ptr) +} + +// At the moment, no cancellable API, just pass nil diff --git a/contrib/golang/glibobject.go.h b/contrib/golang/glibobject.go.h new file mode 100644 index 00000000..a55bd242 --- /dev/null +++ b/contrib/golang/glibobject.go.h @@ -0,0 +1,17 @@ +#include + +static char * +_g_error_get_message (GError *error) +{ + g_assert (error != NULL); + return error->message; +} + +static const char * +_g_variant_lookup_string (GVariant *v, const char *key) +{ + const char *r; + if (g_variant_lookup (v, key, "&s", &r)) + return r; + return NULL; +} diff --git a/contrib/golang/ostree.go b/contrib/golang/ostree.go new file mode 100644 index 00000000..0a60ef68 --- /dev/null +++ b/contrib/golang/ostree.go @@ -0,0 +1,94 @@ +// +build linux + +// Public API specification for libostree Go bindings + +package ostree + +import ( + "unsafe" +) + +// #cgo pkg-config: ostree-1 +// #include +// #include +// #include +// #include "ostree.go.h" +import "C" + +type Repo struct { + *GObject +} + +func RepoGetType() GType { + return GType(C.ostree_repo_get_type()) +} + +func (r *Repo) native() *C.OstreeRepo { + return (*C.OstreeRepo)(r.ptr) +} + +func repoFromNative(p *C.OstreeRepo) *Repo { + if p == nil { + return nil + } + o := GObjectNew(unsafe.Pointer(p)) + r := &Repo{o} + return r +} + +func RepoNewOpen(path string) (*Repo, error) { + var cerr *C.GError = nil + cpath := C.CString(path) + pathc := C.g_file_new_for_path(cpath); + defer C.g_object_unref(C.gpointer(pathc)) + crepo := C.ostree_repo_new(pathc) + repo := repoFromNative(crepo); + r := GoBool(C.ostree_repo_open(repo.native(), nil, &cerr)) + if !r { + return nil, ConvertGError(cerr) + } + return repo, nil +} + +func (r *Repo) GetParent() *Repo { + return repoFromNative(C.ostree_repo_get_parent(r.native())) +} + +type ObjectType int + +const ( + OBJECT_TYPE_FILE ObjectType = C.OSTREE_OBJECT_TYPE_FILE + OBJECT_TYPE_DIR_TREE = C.OSTREE_OBJECT_TYPE_DIR_TREE + OBJECT_TYPE_DIR_META = C.OSTREE_OBJECT_TYPE_DIR_META + OBJECT_TYPE_COMMIT = C.OSTREE_OBJECT_TYPE_COMMIT + OBJECT_TYPE_TOMBSTONE_COMMIT = C.OSTREE_OBJECT_TYPE_TOMBSTONE_COMMIT +) + +func (repo *Repo) LoadVariant(t ObjectType, checksum string) (*GVariant, error) { + var cerr *C.GError = nil + var cvariant *C.GVariant = nil + + r := GoBool(C.ostree_repo_load_variant(repo.native(), C.OstreeObjectType(t), C.CString(checksum), &cvariant, &cerr)) + if !r { + return nil, ConvertGError(cerr) + } + variant := GVariantNew(unsafe.Pointer(cvariant)) + return variant, nil +} + +func (repo *Repo) ResolveRev(ref string) (string, error) { + var cerr *C.GError = nil + var crev *C.char = nil + + r := GoBool(C.ostree_repo_resolve_rev(repo.native(), C.CString(ref), GBool(true), &crev, &cerr)) + if !r { + return "", ConvertGError(cerr) + } + defer C.free(unsafe.Pointer(crev)) + return C.GoString(crev), nil +} + +func (commit *GVariant) CommitGetMetadataKeyString(key string) (string, error) { + cmeta := GVariantNew(unsafe.Pointer(C.g_variant_get_child_value(commit.native(), 0))) + return cmeta.LookupString(key) +} diff --git a/contrib/golang/ostree.go.h b/contrib/golang/ostree.go.h new file mode 100644 index 00000000..b1a15517 --- /dev/null +++ b/contrib/golang/ostree.go.h @@ -0,0 +1,21 @@ +#include +#include + +static void +_ostree_repo_checkout_options_init_docker_union (OstreeRepoCheckoutOptions *opts) +{ + memset (opts, 0, sizeof (*opts)); + opts->mode = OSTREE_REPO_CHECKOUT_MODE_USER; + opts->overwrite_mode = OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES; + opts->disable_fsync = 1; + opts->process_whiteouts = 1; +} + +static const char * +_g_variant_lookup_string (GVariant *v, const char *key) +{ + const char *r; + if (g_variant_lookup (v, key, "&s", &r)) + return r; + return NULL; +} diff --git a/contrib/golang/ostree_test.go b/contrib/golang/ostree_test.go new file mode 100644 index 00000000..6b687d8b --- /dev/null +++ b/contrib/golang/ostree_test.go @@ -0,0 +1,55 @@ +// +build linux + +// Public API specification for libostree Go bindings + +package ostree + +import ( + "testing" +) + +func TestTypeName(t *testing.T) { + name := RepoGetType().Name(); + if name != "OstreeRepo" { + t.Errorf("%s != OstreeRepo"); + } +} + +func TestRepoNew(t *testing.T) { + r, err := RepoNewOpen("/ostree/repo") + if err != nil { + t.Errorf("%s", err); + return + } + parent := r.GetParent() + if parent != nil { + t.Errorf("Expected no parent") + return + } +} + +func TestRepoGetMetadataVersion(t *testing.T) { + r, err := RepoNewOpen("/ostree/repo") + if err != nil { + t.Errorf("%s", err); + return + } + commit,err := r.ResolveRev("rhel-atomic-host/7/x86_64/standard") + if err != nil { + t.Errorf("%s", err) + return + } + commitv,err := r.LoadVariant(OBJECT_TYPE_COMMIT, commit) + if err != nil { + t.Errorf("%s", err) + return + } + ver, err := commitv.CommitGetMetadataKeyString("version") + if err != nil { + t.Errorf("%s", err) + return + } + if ver != "7.1.3" { + t.Errorf("expected 7.1.3") + } +}