This is an automated email from the git hooks/post-receive script.
git pushed a commit to branch main
in repository ego.
View the commit online.
commit 56b01c4cb07f0093d1b2c87d0b07e107454c22d9
Author: [email protected] <[email protected]>
AuthorDate: Mon Mar 30 16:40:27 2026 -0600
feat(ego-gen): add global function support with package-level generation
Add GlobalFunctionData struct, globalFunctionSet for 24 receiver-less
EFL functions, and template section generating package-level Go
functions instead of methods.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
---
cmd/ego-gen/generator.go | 12 ++++
cmd/ego-gen/generator_test.go | 113 ++++++++++++++++++++++++++++++++++++
cmd/ego-gen/global_function.go | 68 ++++++++++++++++++++++
cmd/ego-gen/templates/class.go.tmpl | 29 +++++++++
4 files changed, 222 insertions(+)
diff --git a/cmd/ego-gen/generator.go b/cmd/ego-gen/generator.go
index ad25351..7de63ff 100644
--- a/cmd/ego-gen/generator.go
+++ b/cmd/ego-gen/generator.go
@@ -36,6 +36,7 @@ type ClassData struct {
IteratorMethods []IteratorMethodData // methods returning Eina_Iterator
HasIterators bool // true if IteratorMethods is non-empty (controls import)
IteratorStructTypes []IteratorMethodData // one entry per distinct struct elem type (for C helpers)
+ GlobalFunctions []GlobalFunctionData // package-level functions with no Eo receiver
}
// ConstructorData describes a constructor option for an EFL class. Each entry
@@ -146,6 +147,17 @@ type IndexedPropertyData struct {
MultiSetValues []MultiValueData
}
+// GlobalFunctionData describes an EFL global (class-level) function with no
+// Eo* receiver. These generate package-level Go functions instead of methods.
+type GlobalFunctionData struct {
+ GoName string // e.g. "EventGlobalFreezeCount"
+ CName string // e.g. "efl_event_global_freeze_count_get"
+ Params []ParamData // input parameters (may be empty)
+ ReturnType string // Go return type (empty if void)
+ ReturnCType string // C return type
+ IsEoReturn bool // true if return type is Eo*
+}
+
// IteratorMethodData describes an EFL method that returns an Eina_Iterator.
// The generated Go method returns iter.Seq[T] wrapping the C iterator lifecycle.
type IteratorMethodData struct {
diff --git a/cmd/ego-gen/generator_test.go b/cmd/ego-gen/generator_test.go
index 5fc642e..ca46415 100644
--- a/cmd/ego-gen/generator_test.go
+++ b/cmd/ego-gen/generator_test.go
@@ -906,3 +906,116 @@ func TestGenerateClass_IteratorMethod(t *testing.T) {
}
}
}
+
+func TestGenerateClass_GlobalFunction(t *testing.T) {
+ g, outDir := newTestGenerator(t)
+
+ data := ClassData{
+ PackageName: "eo",
+ GoName: "Object",
+ EolianName: "Efl.Object",
+ CClassName: "EFL_OBJECT_CLASS",
+ CClassGetFunc: "efl_object_class_get",
+ CPrefix: "efl_object",
+ EOHeaderFile: "efl_object.eo.h",
+ IsAbstract: true,
+ GlobalFunctions: []GlobalFunctionData{
+ {
+ GoName: "EventGlobalFreezeCount",
+ CName: "efl_event_global_freeze_count_get",
+ ReturnType: "uint",
+ ReturnCType: "unsigned int",
+ },
+ {
+ GoName: "EventGlobalThaw",
+ CName: "efl_event_global_thaw",
+ },
+ },
+ }
+
+ if err := g.GenerateClass(data); err != nil {
+ t.Fatalf("GenerateClass: %v", err)
+ }
+
+ outFile := filepath.Join(outDir, "eo", "object.go")
+ content, err := os.ReadFile(outFile)
+ if err != nil {
+ t.Fatalf("output file not found at %s: %v", outFile, err)
+ }
+
+ src := string(content)
+
+ mustContain := []string{
+ "func EventGlobalFreezeCount() uint",
+ "efl_event_global_freeze_count_get",
+ "func EventGlobalThaw()",
+ "efl_event_global_thaw",
+ }
+
+ mustNotContain := []string{
+ "(o *Object) EventGlobalFreezeCount",
+ "(o *Object) EventGlobalThaw",
+ }
+
+ for _, want := range mustContain {
+ if !strings.Contains(src, want) {
+ t.Errorf("output missing expected pattern %q\nfile content:\n%s", want, src)
+ }
+ }
+ for _, banned := range mustNotContain {
+ if strings.Contains(src, banned) {
+ t.Errorf("output contains banned pattern %q", banned)
+ }
+ }
+}
+
+func TestGenerateClass_GlobalFunctionString(t *testing.T) {
+ g, outDir := newTestGenerator(t)
+
+ data := ClassData{
+ PackageName: "eo",
+ GoName: "TextMarkupUtil",
+ EolianName: "Efl.Text_Markup_Util",
+ CClassName: "EFL_TEXT_MARKUP_UTIL_CLASS",
+ CClassGetFunc: "efl_text_markup_util_class_get",
+ CPrefix: "efl_text_markup_util",
+ EOHeaderFile: "efl_text_markup_util.eo.h",
+ IsAbstract: true,
+ GlobalFunctions: []GlobalFunctionData{
+ {
+ GoName: "TextToMarkup",
+ CName: "efl_text_markup_util_text_to_markup",
+ Params: []ParamData{
+ {GoName: "text", GoType: "string", CType: "const char *"},
+ },
+ ReturnType: "string",
+ ReturnCType: "const char *",
+ },
+ },
+ }
+
+ if err := g.GenerateClass(data); err != nil {
+ t.Fatalf("GenerateClass: %v", err)
+ }
+
+ outFile := filepath.Join(outDir, "eo", "text_markup_util.go")
+ content, err := os.ReadFile(outFile)
+ if err != nil {
+ t.Fatalf("output file not found at %s: %v", outFile, err)
+ }
+
+ src := string(content)
+
+ mustContain := []string{
+ "func TextToMarkup(text string) string",
+ "C.CString(text)",
+ "C.GoString(",
+ "efl_text_markup_util_text_to_markup",
+ }
+
+ for _, want := range mustContain {
+ if !strings.Contains(src, want) {
+ t.Errorf("output missing expected pattern %q\nfile content:\n%s", want, src)
+ }
+ }
+}
diff --git a/cmd/ego-gen/global_function.go b/cmd/ego-gen/global_function.go
new file mode 100644
index 0000000..75ec863
--- /dev/null
+++ b/cmd/ego-gen/global_function.go
@@ -0,0 +1,68 @@
+package main
+
+// globalFunctionSet contains C function names that are global (no Eo* first
+// parameter). These are routed through the global function codegen path
+// instead of being skipped.
+var globalFunctionSet = map[string]bool{
+ "efl_event_global_thaw": true,
+ "efl_event_global_freeze": true,
+ "efl_event_global_freeze_count_get": true,
+ "efl_text_markup_util_text_to_markup": true,
+ "efl_text_markup_util_markup_to_text": true,
+ "efl_env_self": true,
+ "efl_animation_default_duration_set": true,
+ "efl_app_main_get": true,
+ "efl_net_ssl_context_default_dialer_get": true,
+ "efl_net_dialer_http_date_parse": true,
+ "efl_net_dialer_http_date_serialize": true,
+ "efl_net_ip_address_sockaddr_set": true,
+ "efl_net_ip_address_create": true,
+ "efl_net_ip_address_create_sockaddr": true,
+ "efl_net_ip_address_parse": true,
+ "efl_net_ip_address_resolve": true,
+ "efl_ui_action_connector_bind_clickable_to_theme": true,
+ "efl_ui_action_connector_bind_clickable_to_object": true,
+ "efl_ui_focus_util_active_manager": true,
+ "efl_ui_spotlight_util_stack_gen": true,
+ "efl_ui_view_factory_create_with_event": true,
+ "efl_ui_win_exit_on_all_windows_closed_set": true,
+ "efl_ui_theme_default_get": true,
+ "efl_access_object_access_root_get": true,
+}
+
+// buildGlobalFunctionData converts an Eolian method Function that has no Eo*
+// receiver into a GlobalFunctionData for package-level code generation.
+func buildGlobalFunctionData(f *Function) GlobalFunctionData {
+ methodName := f.Name()
+ goName := SnakeToCamel(methodName)
+ cName := f.FullCName(FunctionMethod)
+
+ var params []ParamData
+ for _, p := range f.Parameters() {
+ if p.Direction() != ParamIn {
+ continue
+ }
+ params = append(params, buildParamData(p))
+ }
+
+ var returnType, returnCType string
+ var isEoReturn bool
+ if rt := f.ReturnType(FunctionMethod); rt != nil {
+ returnCType = rt.CType()
+ returnType = CTypeToGo(returnCType)
+ isEoReturn = rt.IsClass()
+ if returnCType == "void" || returnType == "" {
+ returnType = ""
+ returnCType = ""
+ }
+ }
+
+ return GlobalFunctionData{
+ GoName: goName,
+ CName: cName,
+ Params: params,
+ ReturnType: returnType,
+ ReturnCType: returnCType,
+ IsEoReturn: isEoReturn,
+ }
+}
diff --git a/cmd/ego-gen/templates/class.go.tmpl b/cmd/ego-gen/templates/class.go.tmpl
index 559bb32..f40cfa9 100644
--- a/cmd/ego-gen/templates/class.go.tmpl
+++ b/cmd/ego-gen/templates/class.go.tmpl
@@ -591,6 +591,35 @@ func (o *{{$.GoName}}) {{$im.GoName}}({{range $i, $p := $im.Params}}{{if $i}}, {
}
{{- end}}
+{{- range $gf := .GlobalFunctions}}
+
+// {{$gf.GoName}} is a package-level EFL function with no object receiver.
+func {{$gf.GoName}}({{range $i, $p := $gf.Params}}{{if $i}}, {{end}}{{$p.GoName}} {{if $p.IsEo}}efl.Objecter{{else}}{{$p.GoType}}{{end}}{{end}}){{if $gf.ReturnType}} {{$gf.ReturnType}}{{end}} {
+{{- range $gf.Params}}
+{{- if needsStringConversion .CType}}
+ c{{.GoName}} := C.CString({{.GoName}})
+ defer C.free(unsafe.Pointer(c{{.GoName}}))
+{{- else if isBoolType .CType}}
+ var c{{.GoName}} C.{{cTypeToCgo .CType}}
+ if {{.GoName}} { c{{.GoName}} = 1 }
+{{- end}}
+{{- end}}
+{{- if $gf.ReturnType}}
+{{- if needsStringConversion $gf.ReturnCType}}
+ return C.GoString(C.{{$gf.CName}}({{range $i, $p := $gf.Params}}{{if $i}}, {{end}}{{template "paramArg" $p}}{{end}}))
+{{- else if isBoolType $gf.ReturnCType}}
+ return C.{{$gf.CName}}({{range $i, $p := $gf.Params}}{{if $i}}, {{end}}{{template "paramArg" $p}}{{end}}) != 0
+{{- else if $gf.IsEoReturn}}
+ return unsafe.Pointer(C.{{$gf.CName}}({{range $i, $p := $gf.Params}}{{if $i}}, {{end}}{{template "paramArg" $p}}{{end}}))
+{{- else}}
+ return {{$gf.ReturnType}}(C.{{$gf.CName}}({{range $i, $p := $gf.Params}}{{if $i}}, {{end}}{{template "paramArg" $p}}{{end}}))
+{{- end}}
+{{- else}}
+ C.{{$gf.CName}}({{range $i, $p := $gf.Params}}{{if $i}}, {{end}}{{template "paramArg" $p}}{{end}})
+{{- end}}
+}
+{{- end}}
+
{{- define "paramArg"}}
{{- if .IsEo}}(*C.Eo)({{.GoName}}.Eo())
{{- else if or (needsStringConversion .CType) (isBoolType .CType)}}c{{.GoName}}
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.