C Bindgen
Automatic C header → Nyra extern fn bindings via libclang — call SQLite, zlib, OpenSSL, and the rest of the C ecosystem without hand-written FFI.
Quick start
# Easiest — known system libraries (raylib, zlib, sqlite3, sdl2)
nyra pkg c add raylib
nyra pkg c remove raylib
nyra pkg c list
# Advanced — any header path
nyra bind c /path/to/header.h --lib foo --update-mod
nyra pkg bind c vendor/api.h --lib mylib --update-mod
Default output: vendor/bindings/{header-stem}.ny with every bindable function from the header. Use --export SYM only if you want a smaller file; delete unused lines manually anytime.
nyra pkg c — one command per library
The recommended way to add system C libraries to a NyraPkg project. No manual -I paths, no Homebrew keg paths, no hand-editing nyra.mod.
nyrapkg init && cd myapp
nyra pkg c add raylib # brew install + full bind + nyra.mod
nyra pkg c add zlib
nyra pkg c add sqlite3
nyra pkg c add sdl2
nyra pkg c list # what's installed in this project
nyra pkg c remove raylib # delete bindings + unlink nyra.mod
import "vendor/bindings/raylib.ny"
What add does automatically
| Step | Action |
|---|---|
| Install | brew install … on macOS (or show apt install hint on Linux) |
| Discover | Resolve keg-only paths ($(brew --prefix raylib)/include) |
| Bindgen | Full header → vendor/bindings/{name}.ny (all bindable symbols) |
| Keywords | C params like in, type → in_, type_ |
| Link | Append link … and link -L …/lib to nyra.mod |
| Track | Write vendor/bindings/c-libs.toml for clean remove |
Built-in catalog
nyra pkg c add … | Homebrew | Linux (apt) | Import |
|---|---|---|---|
raylib | brew install raylib | libraylib-dev | vendor/bindings/raylib.ny |
zlib | brew install zlib | zlib1g-dev | vendor/bindings/zlib.ny |
sqlite3 (alias sqlite) | brew install sqlite | libsqlite3-dev | vendor/bindings/sqlite3.ny |
sdl2 | brew install sdl2 | libsdl2-dev | vendor/bindings/SDL.ny |
Flags
| Flag | Description |
|---|---|
--path DIR | Project root (default .) |
--no-install | Skip brew install; fail if library missing |
Manifest (vendor/bindings/c-libs.toml)
# Managed by nyra pkg c add|remove
[raylib]
bindings = "vendor/bindings/raylib.ny"
link = "raylib"
header = "/opt/homebrew/opt/raylib/include/raylib.h"
link_search = ["/opt/homebrew/opt/raylib/lib"]
Re-running nyra pkg c add raylib refreshes bindings. Unknown libraries still work via manual nyra pkg bind c.
Prerequisites
brew install llvm # libclang for bindgen
nyra toolchain install # alternative
import "vendor/bindings/zlib.ny"import "vendor/bindings/zlib.ny"Why Nyra + C libraries
Nyra targets the same niche as Rust or Go calling native code: your app logic stays in Nyra, mature C libraries do the heavy lifting (compression, crypto, databases, OS APIs). C Bindgen removes hand-written extern fn boilerplate — one command turns a .h file into typed Nyra bindings plus nyra.mod link lines.
| Step | Command / file | Result |
|---|---|---|
| 1 | nyra pkg c add raylib | Install + bind + link (one command) |
| 2 | import "vendor/bindings/raylib.ny" | Call C from Nyra like any module |
| 3 | nyra run . | Links native lib and runs |
Tutorial: zlib
Recommended:
nyrapkg init && cd myapp
nyra pkg c add zlib
nyra run .
import "vendor/bindings/zlib.ny"
fn main() {
print(zlibVersion())
}import "vendor/bindings/zlib.ny"
fn main() -> void {
print(zlibVersion())
}Manual bind (advanced — custom headers or unknown libraries)
On Apple Silicon, Homebrew installs zlib as keg-only under /opt/homebrew/opt/zlib.
1 — Prerequisites
brew install zlib llvm
nyrapkg init
cd myapp
2 — Generate bindings
Pass include paths so libclang finds system headers (stddef.h, etc.) and the zlib header:
SDK=$(xcrun --show-sdk-path)
nyra pkg bind c /opt/homebrew/opt/zlib/include/zlib.h --lib z \
-I /opt/homebrew/opt/zlib/include \
-I "$SDK/usr/include" \
-I "$(brew --prefix llvm)/lib/clang/$(ls $(brew --prefix llvm)/lib/clang | tail -1)/include" \
-o vendor/bindings/zlib.ny
Emits all ~267 zlib functions. C parameter names that clash with Nyra keywords (e.g. in) are auto-renamed to in_. Optional: pass --export zlibVersion (repeatable) to emit only symbols you need.
Preview without writing files:
nyra pkg bind c /opt/homebrew/opt/zlib/include/zlib.h --lib z \
-I /opt/homebrew/opt/zlib/include -I "$SDK/usr/include" \
--export zlibVersion --stdout
3 — Fix nyra.mod link paths
With nyra pkg c add this is automatic. For manual bind, ensure link -L points at the lib directory:
module example.local
link z
link -L /opt/homebrew/opt/zlib/lib
Linux (Debian/Ubuntu): link -L /usr/lib/x86_64-linux-gnu · Intel Mac Homebrew: /usr/local/opt/zlib/lib.
4 — Call zlib from Nyra
import "vendor/bindings/zlib.ny"
fn main() {
print("Hello, World!")
print(zlibVersion()) // e.g. "1.3.2"
}import "vendor/bindings/zlib.ny"
fn main() -> void {
print("Hello, World!")
print(zlibVersion()) // e.g. "1.3.2"
}5 — Run
nyra run .
Expected output:
Hello, World!
1.3.2
Project layout after bindgen
myapp/
main.ny
nyra.mod # link z + link -L …/lib
vendor/
bindings/
zlib.ny # auto-generated extern fn + repr(C) structsmyapp/
main.ny
nyra.mod # link z + link -L …/lib
vendor/
bindings/
zlib.ny # auto-generated extern fn + repr(C) structsTutorial: SQLite
nyra pkg c add sqlite3
import "vendor/bindings/sqlite3.ny"
fn main() {
print(sqlite3_libversion())
}
Alternatively: nyrapkg install ny-sqlite@^0.1.0 — see nyrapkg.
Tutorial: Raylib graphics & games 🎮
Raylib — 2D/3D games in C. One command gives you the full API (~556 functions):
nyra pkg c add raylib
import "vendor/bindings/raylib.ny"
Homebrew path pitfalls (/opt/homebrew/include/raylib.h vs keg-only) are handled automatically.
Game loop example
import "vendor/bindings/raylib.ny"
fn main() {
InitWindow(800, 450, "Nyra + Raylib")
SetTargetFPS(60)
let r = 255
let g = 255
let b = 255
let a = 255
let white = Color { r: r, g: g, b: b, a: a }
let r2 = 255
let g2 = 0
let b2 = 0
let a2 = 255
let red = Color { r: r2, g: g2, b: b2, a: a2 }
while !WindowShouldClose() {
BeginDrawing()
ClearBackground(white)
DrawText("Nyra is Drawing Graphics!", 190, 200, 20, red)
DrawCircle(400, 300, 50.0, red)
EndDrawing()
}
CloseWindow()
}import "vendor/bindings/raylib.ny"
fn main() -> void {
InitWindow(800, 450, "Nyra + Raylib")
SetTargetFPS(60)
let r: i32 = 255
let g: i32 = 255
let b: i32 = 255
let a: i32 = 255
let white = Color { r: r, g: g, b: b, a: a }
let r2: i32 = 255
let g2: i32 = 0
let b2: i32 = 0
let a2: i32 = 255
let red = Color { r: r2, g: g2, b: b2, a: a2 }
while !WindowShouldClose() {
BeginDrawing()
ClearBackground(white)
DrawText("Nyra is Drawing Graphics!", 190, 200, 20, red)
DrawCircle(400, 300, 50.0, red)
EndDrawing()
}
CloseWindow()
}Colors: Raylib’s Color uses u8 fields — use let r: u8 = 255 or build via GetColor(hex as u32). Nyra does not parse 0xFF0000FF integer literals yet; use decimal + as u32 or struct fields.
5 — Run
nyra run .
Opens an 800×450 window with text and a red circle at 60 FPS. Runnable example: examples/c_raylib/.
Other simple graphics C libraries
- SDL2 — 2D pixels and input; bind
SDL.hwith--lib SDL2 - stb_image — single-header PNG load/save; use
link-source vendor/stb_image_impl.c
Linux quick reference
# Debian / Ubuntu — install dev packages first
sudo apt install libraylib-dev zlib1g-dev libsqlite3-dev llvm-dev
nyra pkg c add raylib
nyra pkg c add zlib
# Or manual bind
nyra pkg bind c /usr/include/zlib.h --lib z -o vendor/bindings/zlib.ny
Troubleshooting
| Error | Fix |
|---|---|
'stddef.h' file not found | Add -I "$SDK/usr/include" (macOS) or install llvm-dev / system headers (Linux) |
library not found for -lz | Add link -L /opt/homebrew/opt/zlib/lib to nyra.mod |
Expected parameter name (e.g. in) | C param is a Nyra keyword — use --export SYM or --prefix to limit symbols |
Wrong link -L for include dirs | Remove auto-added include link -L lines; keep only the .lib / .dylib directory |
header not found: /opt/homebrew/include/… | Keg-only formula — use $(brew --prefix NAME)/include/header.h (e.g. raylib, zlib) |
Your own C code (link-source)
When you write C alongside Nyra (not only a prebuilt system lib):
vendor/
api.h # C header
api_impl.c # your C implementation
bindings/api.ny # from bindgen or hand-written extern fn
# nyra.mod
link-source vendor/api_impl.c
nyra build compiles link-source files to cached .o objects automatically. See examples/c_bindgen/.
What gets generated
repr(C)structs for C structs with ABI-safe fields (i32,ptr, …)extern fndeclarations for each bindable C functionlink …lines fornyra.mod(with--update-mod)vendor/bindings/shim.c+link-sourcewhen a signature needs a simplified wrapper (opaque struct returns, etc.)
Example output
struct Point repr(C) {
x: i32
y: i32
}
extern fn make_point(x, y) -> Point
extern fn geom_add(a, b) -> i32struct Point repr(C) {
x: i32
y: i32
}
extern fn make_point(x, y) -> Point
extern fn geom_add(a, b) -> i32CLI reference
| Flag | Description |
|---|---|
--lib NAME | Append link NAME to nyra.mod (repeatable) |
-I DIR | Clang include path (repeatable) |
-D MACRO | Preprocessor define (repeatable) |
-o FILE | Output .ny path |
--prefix P | Only functions starting with P |
--export SYM | Optional shrink filter — default binds all symbols |
--shim | Experimental C wrappers for ~25 complex Raylib-style signatures |
--no-shim | Disable shims even when --shim is set |
--update-mod | Append missing link / link-source lines |
--stdout | Print bindings; do not write files |
--no-shim | Skip auto C shim generation |
--project DIR | Project root (nyra bind c only) |
--path DIR | Project root (nyra pkg bind c) |
Type mapping (C → Nyra)
Integer mappings use Nyra’s full i8–i128 / u8–u128 families (optional annotations in your code). See Data types.
| C | Nyra |
|---|---|
void | void |
signed char | i8 |
unsigned char | u8 |
short | i16 |
unsigned short | u16 |
int | i32 |
unsigned / unsigned int | u32 |
long / long long | i64 |
unsigned long / unsigned long long | u64 |
float / double | f64 |
bool / _Bool | bool |
const char * | string |
| C struct (complete, ABI-safe fields) | repr(C) struct Name { … } |
| C enum | i32 |
| pointers, fn pointers | ptr |
Performance
- Nyra compiler — unchanged; bindings are just
extern fndeclarations. - At run time — each call is a direct native FFI jump; overhead is negligible vs the C library work (e.g. SQLite, zlib).
- Shims — when bindgen emits
vendor/bindings/shim.c, one extra C wrapper call for complex signatures only.
C library speed is the library’s own speed — Nyra does not interpret or sandbox it.
Ownership, escape analysis & safety
Nyra’s safety rules apply to your Nyra code at the FFI boundary, not inside third-party C:
| Mechanism | FFI behavior |
|---|---|
| Typecheck | extern fn params/returns must use allowed ABI types (i8…u128, string, ptr, repr(C) struct, …). |
| Move / auto-drop | string returned from C is heap-owned; Nyra auto-frees at scope end (same as read_file). |
ptr | Copy opaque handle — you manage C-side free / close (e.g. sqlite3_close). |
| Escape analysis | Classifies Nyra locals passed into extern fn (e.g. ArgEscape); does not analyze C library internals. |
| Borrow checker | Applies to Nyra variables you pass across the boundary. |
See FFI & ABI · Escape analysis · Memory & ownership.
Workflow
- Vendor or point at the C header (
vendor/foo.h). - Run
nyra pkg bind c vendor/foo.h --lib foo --update-mod. import "vendor/bindings/foo.ny"and call C APIs from Nyra.- Re-run bindgen when the upstream header changes.
Example projects
| Example | What it shows | Run |
|---|---|---|
examples/c_bindgen/ | Custom C header + link-source shim | cd examples/c_bindgen && nyra run . |
examples/ffi/call_libc/ | Hand-written extern fn to libc | cd examples/ffi/call_libc && nyra run . |
| Raylib tutorial above | 2D window + game loop via C Bindgen | cd examples/c_raylib && nyra run . |
examples/c_raylib/ | Raylib graphics demo | brew install raylib && nyra run . |