Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More changes for generating gstreamer bindings #159

Draft
wants to merge 5 commits into
base: 4
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions gir/girgen/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,12 @@ func (f *GoFileGenerator) Generate() ([]byte, error) {
fpen.Words("// #cgo CFLAGS: -Wno-deprecated-declarations")
}

if defines := f.CDefines(); len(defines) > 0 {
for _, macro := range defines {
fpen.Linef("// #define %s", macro)
}
}

fpen.Words("// #include <stdlib.h>")
if incls := f.CIncludes(); len(incls) > 0 {
for _, incl := range incls {
Expand Down Expand Up @@ -317,6 +323,12 @@ func (f *GoFileGenerator) CIncludes() []string {
return namespaceCIncludes(f.current, &f.header)
}

// CIncludes returns this file's sorted C includes, including the repository's C
// includes.
func (f *GoFileGenerator) CDefines() []string {
return f.header.SortedCDefines()
}

func namespaceCIncludes(n *gir.NamespaceFindResult, h *file.Header) []string {
extraIncludes := h.SortedCIncludes()

Expand Down
25 changes: 25 additions & 0 deletions gir/girgen/file/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ type Header struct {
Marshalers []Marshaler
Imports map[string]string
CIncludes map[string]struct{}
Defines map[string]struct{}
Packages map[string]struct{} // for pkg-config
Callbacks map[string]struct{} // used for C blocks in general
CallbackDelete bool
Expand Down Expand Up @@ -273,6 +274,30 @@ func (h *Header) SortedCIncludes() []string {
return includes
}

// DefineC defines a macro using #define
func (h *Header) DefineC(macro string) {
if h.stop {
return
}

if h.Defines == nil {
h.Defines = map[string]struct{}{}
}

h.Defines[macro] = struct{}{}
}

// SortedCDefines returns the list of C defines sorted.
func (h *Header) SortedCDefines() []string {
defines := make([]string, 0, len(h.Defines))
for macro := range h.Defines {
defines = append(defines, macro)
}

sort.Strings(defines)
return defines
}

// NeedsGLibObject adds the glib-object.h include and the glib-2.0 package.
func (h *Header) NeedsGLibObject() {
// Need this for g_value_get_boxed.
Expand Down
6 changes: 6 additions & 0 deletions gir/girgen/generators/iface/iface.go
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,12 @@ func (g *Generator) Use(typ interface{}) bool {
}

for i, sig := range signals {
// signals with the G_SIGNAL_ACTION flag are there to be freely emitted by user code. They also can
// be thought of as methods on generic objects.
if sig.Action {
diamondburned marked this conversation as resolved.
Show resolved Hide resolved
continue // TODO: generate a type safe call for emit instead, similar to how we do for connect
}

// A signal has 2 implied parameters: the instance (0th) parameter and
// the final user_data parameter.
param := &gir.Parameters{
Expand Down
39 changes: 27 additions & 12 deletions gir/girgen/types/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,27 +143,42 @@ func PreserveGetName(girType string) Preprocessor {
})
}

// RenameEnumMembers renames all members of the matched enums. It is primarily
// RenameEnumMembers renames the c-identifier of members of the matched enum or bitfield. It is primarily
// used to avoid collisions.
func RenameEnumMembers(enum, regex, replace string) Preprocessor {
girTypeMustBeVersioned(enum)
re := regexp.MustCompile(regex)
return MapMembers(enum, func(member gir.Member) gir.Member {
parts := strings.SplitN(member.CIdentifier, "_", 2)
parts[1] = re.ReplaceAllString(parts[1], replace)
member.CIdentifier = parts[0] + "_" + parts[1]

return member
})
}

// MapMembers allows you to freely change any property of the members of the given enum or bitfield type.
// it can be used to fix faulty girs, but also to change the behavior of the generator.
func MapMembers(enumOrBitfieldType string, fn func(member gir.Member) gir.Member) Preprocessor {
girTypeMustBeVersioned(enumOrBitfieldType)
return PreprocessorFunc(func(repos gir.Repositories) {
result := repos.FindFullType(enum)
result := repos.FindFullType(enumOrBitfieldType)
if result == nil {
log.Printf("GIR enum %q not found", enum)
log.Panicf("GIR enum or bitfield %q not found", enumOrBitfieldType)
return
}

enum, ok := result.Type.(*gir.Enum)
if !ok {
log.Panicf("GIR type %T is not enum", result.Type)
}

for i, member := range enum.Members {
parts := strings.SplitN(member.CIdentifier, "_", 2)
parts[1] = re.ReplaceAllString(parts[1], replace)
enum.Members[i].CIdentifier = parts[0] + "_" + parts[1]
switch v := result.Type.(type) {
case *gir.Enum:
for i, member := range v.Members {
v.Members[i] = fn(member)
}
case *gir.Bitfield:
for i, member := range v.Members {
v.Members[i] = fn(member)
}
default:
log.Panicf("GIR type %T is not enum or bitfield", result.Type)
}
})
}
Expand Down
47 changes: 47 additions & 0 deletions gir/girgen/types/typeconv/c-go.go
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,53 @@ func (conv *Converter) cgoConverter(value *ValueConverted) bool {
}
return true

case "Gst.MiniObject", "Gst.Structure", "Gst.Caps", "Gst.Buffer", "Gst.BufferList", "Gst.Memory", "Gst.Message", "Gst.Query", "Gst.Sample":
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI I'm likely not going to accept this PR as-is. This diff is a good study for me to add additional APIs into the GIR generator that would permit this for external generators, but adding edge cases from external generators into gotk4 directly is not sustainable in the long term.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please keep this diff around so I can go back and reference it, but I would strongly recommend moving this to a draft PR so that it doesn't block this PR from being merged.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm completely on your side. I'd like to move the cases for the special types into the go-gst repo, and add some functionality to this PR that allows me to hook into this function.

I'm not quite sure how to do this though, because this is fairly deep into the call stack and quite far from the genmain.Data. If you have a suggestion please let me know.

value.header.Import("runtime")
value.header.Import("unsafe")
value.header.ImportCore("gextras")

// Require 1 pointer to avoid weird copies.
value.vtmpl(`
<.Out.Set> = <.OutCast 1>(gextras.NewStructNative(unsafe.Pointer(<.InNamePtr 1>)))
`)
if value.fail {
value.Logln(logger.Debug, "record set fail")
return false
}

if value.TransferOwnership.TransferOwnership == "none" {
value.vtmpl("C.gst_mini_object_ref((*C.GstMiniObject)(unsafe.Pointer(<.InNamePtr 1>)))")
}

if value.TransferOwnership.TransferOwnership != "borrow" {
value.vtmpl(`
runtime.SetFinalizer(
gextras.StructIntern(unsafe.Pointer(<.OutInPtr 1><.OutName>)),
func(intern *struct{ C unsafe.Pointer }) {
C.gst_mini_object_unref((*C.GstMiniObject)(intern.C))
})
`)
}

if value.TransferOwnership.TransferOwnership == "borrow" && len(conv.Results) == 0 {
panic("result should borrow even though callable has no param")
}

if value.TransferOwnership.TransferOwnership == "borrow" {
// the returned value must keep the instance alive, which is the first passed param, aka the receiver
value.vtmpl(fmt.Sprintf(`
runtime.SetFinalizer( // value is borrowed, don't clean up the receiver until dropped
gextras.StructIntern(unsafe.Pointer(<.OutInPtr 1><.OutName>)),
func(_ *struct{ C unsafe.Pointer }) {
runtime.KeepAlive(%s)
},
)`,
conv.Results[0].InName,
))
}

return true

case "GObject.Object", "GObject.InitiallyUnowned":
return value.cgoSetObject(conv)

Expand Down
6 changes: 3 additions & 3 deletions gir/girgen/types/typeconv/go-c.go
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@ func (conv *Converter) gocConverter(value *ValueConverted) bool {

value.header.NeedsExternGLib()
value.p.Linef(
"%s = (*C.GClosure)(coreglib.NewClosure(coreglib.InternObject(%s), %s))",
"%s = (*C.GClosure)(coreglib.NewClosure(coreglib.BaseObject(%s), %s))",
value.Out.Set, instance.In.Name, value.In.Name,
)

Expand Down Expand Up @@ -605,15 +605,15 @@ func (conv *Converter) gocConverter(value *ValueConverted) bool {
value.header.Import("unsafe")
value.header.NeedsExternGLib()
value.p.Linef(
"%s = %s(unsafe.Pointer(coreglib.InternObject(%s).Native()))",
"%s = %s(unsafe.Pointer(coreglib.BaseObject(%s).Native()))",
value.Out.Set, value.OutCast(1), value.InNamePtrPubl(1),
)

if !value.ShouldFree() {
// Caller is taking ownership, which means it will steal our
// reference. Ensure that we take our own.
value.p.Linef(
"C.g_object_ref(C.gpointer(coreglib.InternObject(%s).Native()))",
"C.g_object_ref(C.gpointer(coreglib.BaseObject(%s).Native()))",
value.InNamePtrPubl(1),
)
}
Expand Down
Loading