Develop with Go
Updated
Create TEN applications using Go language binding, including setup, building, debugging, and CGO integration.
This guide shows you how to create and build Go applications using the TEN Framework's Go language binding.
This guide shows you how to create and build Go applications using the TEN Framework's Go language binding.
Create a TEN Go application
Create a new TEN application using Go with the following steps:
Generate the application
Use TEN Manager to create a new Go application from the default template:
tman create app my_go_app --template default_app_go
cd my_go_appInstall dependencies
Install the required TEN packages and extensions:
tman install protocol msgpack
tman install extension_group default_extension_group
tman install extension default_extension_goInstall a default TEN extension
To install a default Go TEN extension:
tman install extension default_extension_goConfigure auto-start graph
Configure your application to automatically start the installed extensions by adding a predefined graph to the property.json file:
"predefined_graphs": [
{
"name": "default",
"auto_start": true,
"nodes": [
{
"type": "extension_group",
"name": "default_extension_group",
"addon": "default_extension_group"
},
{
"type": "extension",
"name": "default_extension_go",
"addon": "default_extension_go",
"extension_group": "default_extension_group"
}
]
}
]Build the application
TEN Go applications require CGO integration, which needs specific environment variables and build configuration. Use the build script provided in the TEN runtime to handle these requirements automatically:
go run ten_packages/system/ten_runtime_go/tools/build/main.goThe build process creates a compiled binary named main in the bin/ directory.
Run the application
Use the provided start script to launch your application with the correct environment variables:
./bin/startThe start script automatically configures the necessary library paths and environment settings for the TEN runtime.
Debug your application
Set up debugging configurations for Visual Studio Code to debug both Go and C code in your TEN application.
Go code debugging
Add this configuration to your .vscode/launch.json file to debug the Go portions of your application:
{
"version": "0.2.0",
"configurations": [
{
"name": "Go app (launch)",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}",
"cwd": "${workspaceFolder}",
"output": "${workspaceFolder}/bin/tmp",
"env": {
"LD_LIBRARY_PATH": "${workspaceFolder}/lib",
"DYLD_LIBRARY_PATH": "${workspaceFolder}/lib",
"CGO_LDFLAGS": "-L${workspaceFolder}/lib -lten_runtime_go -Wl,-rpath,@loader_path/lib"
}
}
]
}C code debugging
Add this configuration to your .vscode/launch.json file to debug the C/CGO portions of your application:
{
"version": "0.2.0",
"configurations": [
{
"name": "C (launch)",
"type": "lldb",
"request": "launch",
"program": "${workspaceFolder}/bin/main",
"cwd": "${workspaceFolder}",
"env": {
"LD_LIBRARY_PATH": "${workspaceFolder}/lib",
"DYLD_LIBRARY_PATH": "${workspaceFolder}/lib",
"CGO_LDFLAGS": "-L${workspaceFolder}/lib -lten_runtime_go -Wl,-rpath,@loader_path/lib"
}
}
]
}CGO integration
Understand generated code
The TEN Framework's Go binding relies on CGO to interface between Go and C code. When you call C functions from Go, the cgo tool automatically converts your Go source files into multiple output files containing both Go and C code. These generated C files are then compiled using standard C compilers like gcc or clang.
You can manually generate and examine these intermediate files using:
mkdir out
go tool cgo -objdir out cmd.go error.goThis creates several generated files that bridge Go and C:
The CGO generation process creates several key files that enable Go-C interoperability:
├── _cgo_export.c
├── _cgo_export.h
├── _cgo_flags
├── _cgo_gotypes.go
├── _cgo_main.c
├── _cgo_.o
├── cmd.cgo1.go
├── cmd.cgo2.c
├── error.cgo1.go
└── error.cgo2.cKey generated files
-
_cgo_export.h: Enables C code to call Go functions
Contains declarations for Go functions exported with the//exportdirective, plus C type definitions that correspond to Go types:typedef signed char GoInt8; typedef unsigned char GoUint8; typedef short GoInt16; typedef unsigned short GoUint16; typedef int GoInt32; typedef unsigned int GoUint32; typedef long long GoInt64; typedef unsigned long long GoUint64; typedef GoInt64 GoInt; typedef GoUint64 GoUint; typedef size_t GoUintptr; typedef float GoFloat32; typedef double GoFloat64; -
_cgo_gotypes.go: Contains Go representations of C types
When you use C types likeC.ten_go_error_tin Go, this file provides the corresponding Go struct definitions:package ten type _Ctype_ten_go_status_t = _Ctype_struct_ten_go_status_t type _Ctype_struct_ten_go_status_t struct { err_no _Ctype_int msg_size _Ctype_uint8_t err_msg [256]_Ctype_char _ [3]byte } -
cmd.cgo1.go: Go wrapper for C function calls
Replaces direct C function calls with CGO-generated wrappers that handle type conversion and memory management:package ten func NewCmd(cmdName string) (Cmd, error) { // ... cStatus := func() _Ctype_struct_ten_go_status_t { var _cgo0 _cgo_unsafe.Pointer = unsafe.Pointer(unsafe.StringData(cmdName)) var _cgo1 _Ctype_int = _Ctype_int(len(cmdName)) _cgoBase2 := &bridge _cgo2 := _cgoBase2 _cgoCheckPointer(_cgoBase2, 0 == 0) return _Cfunc_ten_go_cmd_create_custom_cmd(_cgo0, _cgo1, _cgo2) }() // ... } -
cmd.cgo2.c: C wrapper for the actual C function
Provides the bridge between Go's calling convention and the native C function:CGO_NO_SANITIZE_THREAD void _cgo_cb1b98e39356_Cfunc_ten_go_cmd_create_custom_cmd(void *v) { struct { const void* p0; int p1; char __pad12[4]; ten_go_msg_t** p2; ten_go_error_t r; } __attribute__((__packed__, __gcc_struct__)) *_cgo_a = v; char *_cgo_stktop = _cgo_topofstack(); __typeof__(_cgo_a->r) _cgo_r; _cgo_tsan_acquire(); _cgo_r = ten_go_cmd_create_cmd(_cgo_a->p0, _cgo_a->p1, _cgo_a->p2); _cgo_tsan_release(); _cgo_a = (void*)((char*)_cgo_a + (_cgo_topofstack() - _cgo_stktop)); _cgo_a->r = _cgo_r; _cgo_msan_write(&_cgo_a->r, sizeof(_cgo_a->r)); }
Call sequence
When calling C.ten_go_cmd_create_cmd() from Go, the execution follows this path:
_Cfunc_ten_go_cmd_create_custom_cmd (auto-generated Go)
|
V
_cgo_runtime_cgocall (Go)
|
V
entrysyscall // switch Go stack to C stack
|
V
_cgo_cb1b98e39356_Cfunc_ten_go_cmd_create_custom_cmd (auto-generated C)
|
V
ten_go_cmd_create_cmd (C)
|
V
exitsyscallWork with incomplete types
The cgo tool generates corresponding Go types based on C header files imported via import "C". When a C struct is opaque (only declared, not defined), CGO creates an incomplete type in Go.
Example of an opaque C struct:
typedef struct ten_go_msg_t ten_go_msg_t;Generated incomplete type in Go:
type _Ctype_ten_go_msg_t = _Ctype_struct_ten_go_msg_t
type _Ctype_struct_ten_go_msg_t _cgopackage.IncompleteIncomplete types have the following limitations:
-
Cannot be allocated directly
Go's garbage collector cannot manage incomplete types, so standard allocation fails:msg := new(C.ten_go_msg_t) // Error: can't be allocated in Go; it is incomplete (or unallocatable) -
Cannot pass pointers directly to C
CGO rules require pointers to incomplete types to be "pinned" for garbage collector safety, making direct pointer passing impossible.
Workaround: Use C.uintptr_t
Instead of working with pointers to incomplete types, use C.uintptr_t to represent C pointers as integers.
This approach has the following benefits:
- No allocation overhead:
uintptrvalues can be passed directly between Go and C - GC-safe: No pointer tracking required since it's an integer value
- Direct conversion: Represents the exact memory address as an integer
Important limitations
- No pointer arithmetic:
uintptris just an integer representing an address - Cannot dereference: Converting to
unsafe.Pointercan break GC assumptions - Use
0for null: Sinceuintptris an integer, use0instead ofnilfor null pointers
// Correct way to represent a null pointer
var nullPtr C.uintptr_t = 0
// Incorrect - nil cannot be used with uintptr
// var badPtr C.uintptr_t = nil // Compilation error