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.
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).
|
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:
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.rsis 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 clonealways 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.
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:
The sysroot is the one synced from the board (see
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 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. 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 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
and an application is not one of bela-sys's.
An application does not read this crate's metadata directly — see the
bela README for the
DEP_BELA_RELAY_LINK_ARGS_* an application's own build.rs reads, and
docs/cross-compile.md for the direct
linker = setting that replaces scripts/aarch64-bela-linker.sh for
anyone depending on the published crates.