bela-sys 0.8.0

Raw FFI bindings to the Bela core API (libbela) for Bela Gem
Documentation
# bela-sys

Raw FFI bindings to the Bela core API (`libbela`) for Bela Gem on
PocketBeagle 2 (`aarch64-unknown-linux-gnu`).

## Vendored headers

`vendor/bela/` contains `Bela.h`, `GPIOcontrol.h` and `Utilities.h`
(the include closure of `wrapper.h`) plus the upstream `LICENSE`. The
provenance is recorded in `vendor/bela/SOURCE`. These files are LGPL
3.0 (see `vendor/bela/LICENSE`); the rest of the crate is MIT OR
Apache-2.0.

`vendor/ne10/` is a second tree and a different kind of thing: it
holds `NE10_dsp.h` and `NE10_types.h` from the board, and **nothing
generates from them**. `src/ne10.rs` declares NE10's four FFT
functions by hand, so those two headers are only the baseline `cargo
xtask check-vendor --board` diffs a board against, together with the
library identity in `vendor/ne10/SOURCE` — a build id and a hash,
because every build of the library calls itself `libNE10.so.10`.
Wiring them into a bindgen run later would be a change of purpose
rather than a tidy-up. `abi/ne10_abi.c` is the same check from the
other side, at build time and without a board: it asserts that the
headers still describe what `src/ne10.rs` declares. NE10 is
BSD-3-Clause, whose notice is at the top of each of those headers.
See [docs/fft.md](../docs/fft.md).

The headers are taken **from the board**, not from
[BelaPlatform/Bela]: the Bela Gem image ships Bela 1.18.0, which is
newer than any published branch (see
[docs/board-facts.md](../docs/board-facts.md)).

```sh
scripts/update-vendor.sh --board          # from root@bela.local
scripts/update-vendor.sh <branch|commit>  # from upstream git
```

Because the pin follows a board image rather than a released version,
a new image moves the headers on the board without changing anything
here. After updating one, compare the two:

```sh
cargo xtask check-vendor --board          # against root@bela.local
```

It reports the `BELA_*_VERSION` macros on both sides and diffs every
vendored file against the board's copy, exiting non-zero on drift —
which is the signal to re-run the update script and regenerate the
bindings below. It needs a board, so CI cannot run it.

### Why vendored files instead of a git submodule

- The include closure is three files (~70 KB); a submodule would drag
  in the whole upstream repository (IDE, examples, PRU firmware,
  history) for every clone and CI run.
- `src/bindings.rs` is committed, and vendoring keeps "these headers"
  and "the bindings generated from them" atomic in one commit — a
  submodule can drift ahead of the generated code, and its bumps show
  up as opaque hash changes instead of reviewable header diffs.
- The pin tracks the exact Bela version shipped on the board, which
  does not correspond to any published upstream commit. File copies
  can come from anywhere; a submodule can only point at upstream
  commits.
- A plain `git clone` always builds — no `--recursive`, no submodule
  initialisation failure modes.

The trade-off is that provenance rests on `scripts/update-vendor.sh`
recording the source in `vendor/bela/SOURCE`, rather than on git
itself.

[BelaPlatform/Bela]: https://github.com/BelaPlatform/Bela

## Regenerating the bindings

`src/bindings.rs` is generated but **committed**, so building this
crate requires neither libclang nor an aarch64 sysroot. Regenerate it
after updating the vendored headers:

```sh
cargo xtask bindgen --sysroot <dir>   # or set BELA_SYSROOT
```

The sysroot is the one synced from the board (see
[docs/cross-compile.md](../docs/cross-compile.md)); bindgen needs it
for the libc headers `Bela.h` includes.

## The MIDI shim

`shim/midi.h` and `shim/midi.cpp` are a C surface over Bela's `Midi`
class, which is C++ and lives in `libbelaextra`. They are the one part
of this crate that is neither generated nor a declaration of Bela's own
C API: Bela ships a partial C surface in `libraries/Midi/Midi_c.h`, and
the shim is that file written again with output, port listing, and
error reporting that distinguishes a port that opened from one that did
not. [`docs/midi.md`](../docs/midi.md) records why, and what the class
does on the audio thread.

`build.rs` compiles it with the `cc` crate when the sysroot carries
Bela's `libraries/Midi` sources, and skips it — with a warning — when
it does not, so that a check without a sysroot still works. The
compiler comes from `BELA_CXX`, or from `BELA_CC` when that ends in
`gcc`, and the archiver follows the compiler's name; see
[docs/cross-compile.md](../docs/cross-compile.md). That choice is the
one piece of the build script with more than one answer, so it lives in
`shim_compiler.rs` and is tested from `src/lib.rs` — `cargo test` does
not build a build script.

The class is LGPL 3.0, like the vendored headers; the shim reaches it
by linking `libbelaextra.so` dynamically.

## Linking

`build.rs` emits the link flags for `libbela` on device targets:
the library search paths from `docs/board-facts.md` (prefixed with
`BELA_SYSROOT` when cross-compiling) and `-lbelaextra -lbela` plus the
C++ runtime and transitive dependencies (`seasocks`, `evl`, `stdc++`)
that Rust does not link on its own.

The order of those two matters: `libbelaextra.so` needs symbols from
`libbela.so` without naming it in its own `DT_NEEDED`, and rustc links
with `--as-needed`, which drops a `libbela` that appears before the
library needing it.

On non-device targets it emits nothing, so host builds and `cargo
check`/`clippy` for the target work without a sysroot.

### Sysroot-specific compiler-driver arguments

A device link also needs `--sysroot`, `-B` and `-Wl,-rpath-link`
arguments derived from `BELA_SYSROOT` — see
[docs/cross-compile.md](../docs/cross-compile.md) for what each is for.
`build.rs` publishes them as `links` metadata (`links = "bela"`) rather
than adding them to its own link line, because they belong to the
*final* link — the application's — not to this crate's. `bela`
(`links = "bela_relay"`) relays them to its own dependents, since
[`links` metadata reaches only an immediate dependent](https://doc.rust-lang.org/cargo/reference/build-scripts.html#the-links-manifest-key)
and an application is not one of `bela-sys`'s.

An application does not read this crate's metadata directly — see the
[`bela` README](../bela/README.md#downstream-setup) for the
`DEP_BELA_RELAY_LINK_ARGS_*` an application's own `build.rs` reads, and
[docs/cross-compile.md](../docs/cross-compile.md) for the direct
`linker =` setting that replaces `scripts/aarch64-bela-linker.sh` for
anyone depending on the published crates.