Generates So extern declarations from C header files.
sobind parses .h files and emits a Go source file with so:extern stubs
for structs, unions, constants, variables, function pointer typedefs, and function declarations.
Usually, the generated bindings are good enough to use as they are, without any manual changes. If sobind can't process a header or generates invalid Go code, please open an issue and I'll take a look.
Installation • Usage • Naming • Notes • Examples
go install solod.dev/sobind@latest
sobind [-o output.go] [-pkg name] [-I dir] [-D name[=value]] [-scope dir] [-body] [-style c|go] [-strip prefix] [-rename file] <header.h | dir> ...
Usage:
-D value
macro to predefine, 'name' or 'name=value' (repeatable)
-I value
include search directory (repeatable)
-body
emit function bodies (default: declaration only)
-o string
output file (default: stdout)
-pkg string
Go package name (default "main")
-rename string
file of 'cname soname' lines that set So names by hand
-scope value
directory whose headers are emitted, beyond the named files (repeatable)
-strip value
C name prefix to remove (repeatable)
-style string
symbol naming: c (keep C names), cap (capitalized), or go (exported CamelCase)
When given a directory, all .h files in it are processed.
By default only the named headers are emitted; anything they include is parsed but skipped, so system headers stay out of the binding. An umbrella header like sodium.h holds no declarations of its own, just includes of the library's sodium/*.h headers, so on its own it emits nothing. Point -scope at the library's header tree to emit every header under it:
sobind -o extern.go -scope libsodium/include libsodium/include/sodium.h
-D defines a macro, like the C compiler flag with the same name. Use it for macros that a header expects you to set, like the one that prevents glfw3.h from including the OpenGL headers:
sobind -o extern.go -D GLFW_INCLUDE_NONE -I /opt/homebrew/include /opt/homebrew/include/GLFW/glfw3.h
-style=c keeps every C name as it is. Most C names are lower case, so the symbols are unexported and only usable inside the generated package.
-style=cap capitalizes every C name for export without otherwise changing it. It's the simplest way to export the binded API from your package.
Combine it with -strip=<prefix> to remove the C name prefix:
uv_buf_init→Buf_initsqlite3_busy_timeout→Busy_timeout
-style=go emits exported CamelCase names.
The rules:
- Remove a trailing
_t:loop_tbecomesLoop. - Split on
_and join the parts in CamelCase:open_v2becomesOpenV2. A part in upper case only is lowered first, soRUN_DEFAULTbecomesRunDefault. Every other part keeps its inner capitals, so the fieldpMethodsbecomesPMethods.
Combine -style=go with -strip=<prefix> to remove the C name prefix:
uv_buf_init→BufInitsqlite3_busy_timeout→BusyTimeout
Using name-modifying flags like -style and -strip can lead to name collisions.
For example, sqlite3.h defines symbols sqlite3_blob and SQLITE_BLOB. With -strip=sqlite3_ -strip=sqlite -style=go both map to Blob .
To solve such collisions, either change the style, drop one of the -strip, or use the -rename=<filename> flag. The rename file maps C names to So names, one pair per line:
sqlite3_blob Blob
SQLITE_BLOB SQLITE_BLOB
A line with a C name without a So name drops the symbol instead:
# The symbol won't be emitted at all.
Fts5Tokenizer
Whenever sobind cannot map a C declaration exactly, it writes a note above it, and counts the notes under the file header:
// Code generated by "so bind"; DO NOT EDIT.
// sobind: 5 opaque, 1 skipped, 1 guessed
Every note is a single line of the form // sobind: <verb> <C name>, <reason>, so grep "sobind:" lists everything in the generated file that may need manual work. The verb says what happened:
opaque— the type is emitted without its fields. C still owns the layout, so the binding is safe, but the So side cannot read the fields.skipped— nothing is emitted for the C symbol. Declare it by hand if you need it.guessed— the declaration is emitted, but not as C declares it. Check it before using it.inlined— the function is emitted as C declares it, but the header defines it with theinlinespecifier alone (withoutstaticorextern). Such a definition provides no symbol of its own, so the call links only if the library carries one.
sobind -o sqlite3.go -pkg main sqlite3.h
sobind -o sdl3.go -I . SDL3
sobind -o libuv.go -pkg libuv -style=cap -strip uv_ uv.h
Here are some bindings for popular C libraries generated with sobind:
Check the Makefiles in these projects for examples of how to build your own.