diamondburned / gotk4

Autogenerated GTK4 bindings for Go
GNU Affero General Public License v3.0
516 stars 20 forks source link

Proposal: Runtime dynamic linking #74

Open diamondburned opened 2 years ago

diamondburned commented 2 years ago

Reasoning

Approaches

Implementation

libgirepository

https://gi.readthedocs.io/en/latest/writingbindings/libgirepository.html

GModule

package gmodule

type Module struct {
    syms   sync.Map
    handle uintptr
}

func MustLoad(name string, flags Flags) *Module

func (m *Module) Symbol(name string, v *unsafe.Pointer) error
func (m *Module) MustSymbol(name string, v *unsafe.Pointer)

///

package gtk

var module = gmodcore.MustLoad("???")

func NewWidget() *Widget {
    var _cfunc unsafe.Pointer
    module.MustLoad("???", &_cfunc)

    w := (*(*C._gotk4_gtk4_new_widget)(_cfunc))()
    return cast(w)
}

dlfcn

package dlsym

type Handle struct{}

func MustOpen()

func (h *Handle) Symbol()

// TBD

Considerations

diamondburned commented 2 years ago

Further Thoughts

diamondburned commented 2 years ago

WIP branch at https://github.com/diamondburned/gotk4-libgirepository.

I'm unsure if gio should be runtime-linked or compile-time-linked. It takes ~1 minute to build it with cmd/cgo.

diamondburned commented 1 year ago

Error log:

# github.com/diamondburned/gotk4/pkg/atk
pkg/atk/atkcomponent.go:142:7: could not determine kind of name for C.AtkRectangle
// NewRectangle creates a new Rectangle instance from the given
// fields. Beware that this function allocates on the Go heap; be careful
// when using it!
func NewRectangle(x, y, width, height int) Rectangle {
    var f0 C.gint // out
    f0 = C.gint(x)
    var f1 C.gint // out
    f1 = C.gint(y)
    var f2 C.gint // out
    f2 = C.gint(width)
    var f3 C.gint // out
    f3 = C.gint(height)

    v := C.AtkRectangle{
        x:      f0,
        y:      f1,
        width:  f2,
        height: f3,
    }

    return *(*Rectangle)(gextras.NewStructNative(unsafe.Pointer(&v)))
}

Fix is to do var b [n]byte and use unsafe.Add(&b[0]). Go will allocate b on the Go heap for us.

Alternative solution Fix is to have Go generate a Go struct like so: ```go type rectangleNative struct { x C.gint y C.gint width C.gint height C.gint } ``` Then we can generate `unsafe.Offsetof` to assert that the offset of these fields match what's declared in the GIR file. Since `unsafe.Offsetof` is a constant, an `if unsafe.Offsetof(x) == N` check can easily be omitted. Cons: - What should we do with nested records? `field [n]byte` maybe? That would be weird.
jwijenbergh commented 1 year ago

Sorry for the noise, but maybe https://github.com/ebitengine/purego could in theory be used for dynamic linking? They have some dlfcn stuff, hopefully that helps

mgord9518 commented 1 year ago

Would this make it possible to have fully static binaries?

LukeShu commented 1 year ago

@mgord9518

Would this make it possible to have fully static binaries?

I don't think so. The binaries wouldn't need to link to libgtk and friends anymore, but they'd still need to link to libdl. (Perhaps that's avoidable with https://github.com/ebitengine/purego though?)

jwijenbergh commented 1 year ago

@mgord9518

Would this make it possible to have fully static binaries?

I don't think so. The binaries wouldn't need to link to libgtk and friends anymore, but they'd still need to link to libdl. (Perhaps that's avoidable with https://github.com/ebitengine/purego though?)

With purego it's also complicated https://github.com/ebitengine/purego/issues/132

diamondburned commented 1 year ago

This proposal doesn't aim to produce fully static binaries, FWIW. My goal was to link to some very basic core libraries (i.e. glib and gobject-introspection), and then link to everything else during runtime.

This proposal has less to do with producing a pure Go binary or a static binary and a lot more to do with optimizing for build times. Implementing this proposal would allow some of the much larger packages (e.g. GTK4) to be in pure Go (linking to already-compiled Cgo packages), speeding up compile times significantly.

T0astBread commented 1 year ago

Sorry for the noob question but is there anything to try out in the 4-libgirepository branch at this point? And if so, does it require any special build flags? (I tried just replacing the regular version with that branch but build times were about the same.)

diamondburned commented 1 year ago

I don't think the runtime linking mode was ever completed or even reached a runnable state. I believe all changes from 4-libgirepository should've already been merged into 4 guarded behind an environment variable.