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

StepAction
Installbrew install … on macOS (or show apt install hint on Linux)
DiscoverResolve keg-only paths ($(brew --prefix raylib)/include)
BindgenFull header → vendor/bindings/{name}.ny (all bindable symbols)
KeywordsC params like in, typein_, type_
LinkAppend link … and link -L …/lib to nyra.mod
TrackWrite vendor/bindings/c-libs.toml for clean remove

Built-in catalog

nyra pkg c add …HomebrewLinux (apt)Import
raylibbrew install rayliblibraylib-devvendor/bindings/raylib.ny
zlibbrew install zlibzlib1g-devvendor/bindings/zlib.ny
sqlite3 (alias sqlite)brew install sqlitelibsqlite3-devvendor/bindings/sqlite3.ny
sdl2brew install sdl2libsdl2-devvendor/bindings/SDL.ny

Flags

FlagDescription
--path DIRProject root (default .)
--no-installSkip 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"

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.

StepCommand / fileResult
1nyra pkg c add raylibInstall + bind + link (one command)
2import "vendor/bindings/raylib.ny"Call C from Nyra like any module
3nyra 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())
}
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"
}

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) structs

Tutorial: 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()
}

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

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

ErrorFix
'stddef.h' file not foundAdd -I "$SDK/usr/include" (macOS) or install llvm-dev / system headers (Linux)
library not found for -lzAdd 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 dirsRemove 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

Example output

struct Point repr(C) {
    x: i32
    y: i32
}

extern fn make_point(x, y) -> Point
extern fn geom_add(a, b) -> i32

CLI reference

FlagDescription
--lib NAMEAppend link NAME to nyra.mod (repeatable)
-I DIRClang include path (repeatable)
-D MACROPreprocessor define (repeatable)
-o FILEOutput .ny path
--prefix POnly functions starting with P
--export SYMOptional shrink filter — default binds all symbols
--shimExperimental C wrappers for ~25 complex Raylib-style signatures
--no-shimDisable shims even when --shim is set
--update-modAppend missing link / link-source lines
--stdoutPrint bindings; do not write files
--no-shimSkip auto C shim generation
--project DIRProject root (nyra bind c only)
--path DIRProject root (nyra pkg bind c)

Type mapping (C → Nyra)

Integer mappings use Nyra’s full i8i128 / u8u128 families (optional annotations in your code). See Data types.

CNyra
voidvoid
signed chari8
unsigned charu8
shorti16
unsigned shortu16
inti32
unsigned / unsigned intu32
long / long longi64
unsigned long / unsigned long longu64
float / doublef64
bool / _Boolbool
const char *string
C struct (complete, ABI-safe fields)repr(C) struct Name { … }
C enumi32
pointers, fn pointersptr

Performance

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:

MechanismFFI behavior
Typecheckextern fn params/returns must use allowed ABI types (i8u128, string, ptr, repr(C) struct, …).
Move / auto-dropstring returned from C is heap-owned; Nyra auto-frees at scope end (same as read_file).
ptrCopy opaque handle — you manage C-side free / close (e.g. sqlite3_close).
Escape analysisClassifies Nyra locals passed into extern fn (e.g. ArgEscape); does not analyze C library internals.
Borrow checkerApplies to Nyra variables you pass across the boundary.

See FFI & ABI · Escape analysis · Memory & ownership.

Workflow

  1. Vendor or point at the C header (vendor/foo.h).
  2. Run nyra pkg bind c vendor/foo.h --lib foo --update-mod.
  3. import "vendor/bindings/foo.ny" and call C APIs from Nyra.
  4. Re-run bindgen when the upstream header changes.

Example projects

ExampleWhat it showsRun
examples/c_bindgen/Custom C header + link-source shimcd examples/c_bindgen && nyra run .
examples/ffi/call_libc/Hand-written extern fn to libccd examples/ffi/call_libc && nyra run .
Raylib tutorial above2D window + game loop via C Bindgencd examples/c_raylib && nyra run .
examples/c_raylib/Raylib graphics demobrew install raylib && nyra run .

See also