Generator Configuration
Overview
WeaveFFI ships with sensible defaults so weaveffi generate api.yml
just works. When you need to override package names, namespaces, or
the C ABI prefix, you have two options that compose with each other:
- A TOML file (
weaveffi.toml) passed via--config. Per-environment values that vary by machine or CI runner. - An inline
generators:block inside the IDL. Project-wide values every contributor inherits without remembering a flag.
When the same option appears in both, the inline IDL value wins.
When to use
- Use the TOML config when one developer or one pipeline needs to swap a value without changing the IDL.
- Use the inline
generators:block when the value is part of the project contract (Swift module name, Go module path, custom C ABI prefix). Checking it into the IDL guarantees consistency. - Use both when there is a project-wide default that an environment occasionally needs to override.
Step-by-step
1. Pass a TOML config file
weaveffi generate api.yml -o generated --config weaveffi.toml
[swift]
module_name = "MyApp"
[android]
package = "com.example.myapp"
[node]
package_name = "@myorg/myapp"
[wasm]
module_name = "myapp_wasm"
[c]
prefix = "myapp"
[global]
strip_module_prefix = true
Every section and key is optional; omit anything you want defaulted.
The [global] table accepts the alias [weaveffi].
2. Embed generators: in the IDL
version: "0.3.0"
modules:
- name: math
functions:
- name: add
params:
- { name: a, type: i32 }
- { name: b, type: i32 }
return: i32
generators:
swift:
module_name: MyAppFFI
android:
package: com.example.myapp
c:
prefix: myapp
cpp:
namespace: myapp
header_name: myapp.hpp
standard: "20"
dart:
package_name: my_dart_pkg
go:
module_path: github.com/example/myapp
ruby:
module_name: MyApp
gem_name: myapp
weaveffi:
strip_module_prefix: true
pre_generate: "cargo build --release"
Unknown target keys are silently ignored, so an older weaveffi CLI
can still read an IDL written for a newer one.
3. Verify the result
weaveffi generate api.yml -o generated --config weaveffi.toml
ls generated/
For day-to-day project recipes:
# iOS / macOS
[swift]
module_name = "MyAppFFI"
[c]
prefix = "myapp"
# Android
[android]
package = "com.example.myapp.ffi"
[c]
prefix = "myapp"
# Node
[node]
package_name = "@myorg/myapp-native"
When you set [c] prefix = ... and do not explicitly set
[cpp] c_prefix = ..., the CLI copies the C prefix into the C++ wrapper
config automatically so the C++ header keeps calling the same symbols
the C ABI exports.
4. Wire it into CI
weaveffi diff --check enforces that the committed bindings still
match the IDL. A typical guard job:
# .github/workflows/ci.yml
- name: Verify generated bindings are up to date
run: weaveffi diff api.yml --out generated --check
weaveffi validate --format json and weaveffi lint --format json
are designed to be parsed by quality dashboards:
weaveffi --quiet validate api.yml --format json | jq '.ok'
weaveffi --quiet lint api.yml --format json > lint-report.json || \
(cat lint-report.json && exit 1)
Reference
TOML config files and inline IDL generators: blocks share the same
section names and key names. Pick the location that fits your workflow;
the keys are identical.
Per-target sections
| Section | Key | Type | Default | Description |
|---|---|---|---|---|
[swift] | module_name | string | "WeaveFFI" | Swift module name in Package.swift and the Sources/ directory |
[swift] | strip_module_prefix | bool | false | Strip the IR module prefix from emitted Swift symbols |
[android] | package | string | "com.weaveffi" | Java/Kotlin package declaration in the JNI wrapper |
[android] | strip_module_prefix | bool | false | Strip the IR module prefix from emitted Java/Kotlin symbols |
[node] | package_name | string | "weaveffi" | npm package name in the Node.js loader |
[node] | strip_module_prefix | bool | false | Strip the IR module prefix from emitted JS/TS symbols |
[wasm] | module_name | string | "weaveffi_wasm" | Module name in the WASM JS loader |
[c] | prefix | string | "weaveffi" | Prefix prepended to every C ABI symbol ({prefix}_{module}_{function}) |
[cpp] | namespace | string | "weaveffi" | C++ namespace for the wrapper |
[cpp] | header_name | string | "weaveffi.hpp" | Header file name for the C++ output |
[cpp] | standard | string | "17" | C++ standard for the generated CMakeLists.txt |
[cpp] | c_prefix | string | inherits [c] | C ABI prefix that the C++ wrappers call into |
[python] | package_name | string | "weaveffi" | Python package name |
[python] | strip_module_prefix | bool | false | Strip the IR module prefix from emitted Python symbols |
[dotnet] | namespace | string | "WeaveFFI" | .NET namespace |
[dotnet] | strip_module_prefix | bool | false | Strip the IR module prefix from emitted C# symbols |
[dart] | package_name | string | "weaveffi" | Dart package name in pubspec.yaml |
[go] | module_path | string | "weaveffi" | Go module path in go.mod |
[ruby] | module_name | string | "WeaveFFI" | Ruby module that wraps the bindings |
[ruby] | gem_name | string | "weaveffi" | Ruby gem name |
[global] section
| Key | Type | Default | Description |
|---|---|---|---|
strip_module_prefix | bool | false | Shorthand: enable strip_module_prefix on every target that supports it |
pre_generate | string | none | Shell command run before any generator starts |
post_generate | string | none | Shell command run after every generator finishes |
The alias [weaveffi] is accepted for the [global] section.
Performance and CI flags
-
The orchestrator dispatches every selected generator in parallel using rayon. The pre- and post-generate hooks still run serially around the whole batch.
-
Each generator persists a hash under
{out_dir}/.weaveffi-cache/{target}.hash. Only generators whose hash changed are re-run; pass--forceto invalidate every entry. -
weaveffi diff --checkexit codes:Code Meaning 0The committed output matches the IDL exactly. 2One or more files would change in place. 3One or more files would be added or removed. -
weaveffi validate --format jsonemits structured success/failure:{ "ok": true, "modules": 2, "functions": 8, "structs": 3, "enums": 1 }{ "ok": false, "errors": [ { "code": "DuplicateFunctionName", "module": "math", "function": "add", "message": "duplicate function name in module 'math': add", "suggestion": "function names must be unique within a module; rename the duplicate" } ] } -
weaveffi lint --format jsonreturns the warning list with stablecode/location/messagefields:{ "ok": false, "warnings": [ { "code": "DeepNesting", "location": "math::compute::matrix", "message": "deep type nesting at math::compute::matrix (depth 4, max recommended 3)" } ] }
Pitfalls
- Inline value overrides TOML silently — there is no warning when both are set. If a TOML override “doesn’t take”, check for an inline block in the IDL.
[c] prefixrewrites every generator — picking a custom prefix also rewrites the runtime symbols ({prefix}_free_string, …). The Rust cdylib must be built with the same prefix. The C++ wrapper picks it up automatically; if you set both[c] prefixand[cpp] c_prefixmake sure they agree.strip_module_prefix = trueflattens names — collisions across modules become possible. Pick one or the other consistently.- Hooks run shell commands as-is —
pre_generateandpost_generateare passed straight tosh -c. Quote them carefully and never include untrusted input. - Cache covers IR, generator name, generator config, and CLI version — changing the IR, any generator config field, or upgrading the CLI invalidates the per-generator cache and triggers re-emission.
- Older CLIs ignore unknown keys — adding a new generator key with a project-wide implication does not error out on older toolchains. Pin the CLI version in CI when you need that guarantee.