Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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

SectionKeyTypeDefaultDescription
[swift]module_namestring"WeaveFFI"Swift module name in Package.swift and the Sources/ directory
[swift]strip_module_prefixboolfalseStrip the IR module prefix from emitted Swift symbols
[android]packagestring"com.weaveffi"Java/Kotlin package declaration in the JNI wrapper
[android]strip_module_prefixboolfalseStrip the IR module prefix from emitted Java/Kotlin symbols
[node]package_namestring"weaveffi"npm package name in the Node.js loader
[node]strip_module_prefixboolfalseStrip the IR module prefix from emitted JS/TS symbols
[wasm]module_namestring"weaveffi_wasm"Module name in the WASM JS loader
[c]prefixstring"weaveffi"Prefix prepended to every C ABI symbol ({prefix}_{module}_{function})
[cpp]namespacestring"weaveffi"C++ namespace for the wrapper
[cpp]header_namestring"weaveffi.hpp"Header file name for the C++ output
[cpp]standardstring"17"C++ standard for the generated CMakeLists.txt
[cpp]c_prefixstringinherits [c]C ABI prefix that the C++ wrappers call into
[python]package_namestring"weaveffi"Python package name
[python]strip_module_prefixboolfalseStrip the IR module prefix from emitted Python symbols
[dotnet]namespacestring"WeaveFFI".NET namespace
[dotnet]strip_module_prefixboolfalseStrip the IR module prefix from emitted C# symbols
[dart]package_namestring"weaveffi"Dart package name in pubspec.yaml
[go]module_pathstring"weaveffi"Go module path in go.mod
[ruby]module_namestring"WeaveFFI"Ruby module that wraps the bindings
[ruby]gem_namestring"weaveffi"Ruby gem name

[global] section

KeyTypeDefaultDescription
strip_module_prefixboolfalseShorthand: enable strip_module_prefix on every target that supports it
pre_generatestringnoneShell command run before any generator starts
post_generatestringnoneShell 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 --force to invalidate every entry.

  • weaveffi diff --check exit codes:

    CodeMeaning
    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 json emits 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 json returns the warning list with stable code / location / message fields:

    {
      "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] prefix rewrites 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] prefix and [cpp] c_prefix make sure they agree.
  • strip_module_prefix = true flattens names — collisions across modules become possible. Pick one or the other consistently.
  • Hooks run shell commands as-ispre_generate and post_generate are passed straight to sh -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.