Runtime support for TUIs packaged as single-file binaries with Burrito.
The CLI module scaffolded by mix ex_ratatui.gen.burrito is a thin shim
that delegates to start_link/3, so fixes to the entry-point protocol
ship with ex_ratatui upgrades instead of freezing in generated consumer
code.
Inside a wrapped binary the TUI runs synchronously, blocking OTP
application startup for its whole lifetime. Burrito boots the release
with :elixir.start_cli, which halts the node the moment the boot's
-s call returns — running the TUI in an async task loses that race and
the binary exits before drawing a frame. Blocking Application.start
keeps the boot (and therefore the VM) alive until the TUI exits, at
which point main/3 stops the VM itself.
verify_linux_nif/1 is a release step that turns the most common
packaging mistake — building the linux target without TARGET_ABI=musl
— into an immediate build error instead of a shipped-broken binary.
Nothing here references Burrito at compile time: the wrapped-binary
check reads the __BURRITO environment variable the burrito wrapper
sets when launching the payload, which its maintainers document as the
supported detection mechanism, so ex_ratatui needs no burrito
dependency.
Summary
Functions
Runs a burrito-wrapped TUI to completion.
Supervised entry point for a burrito-wrapped TUI — the scaffolded CLI's
start_link/1 delegates here.
Release step that fails the build when the linux burrito target bundles a glibc NIF.
Functions
Runs a burrito-wrapped TUI to completion.
Boots tui_module (any module using ExRatatui.App), waits for it to
exit, then stops the VM with a matching exit code so the wrapper
returns control to the shell: 0 after a clean exit, 1 when the TUI
crashes, fails to start (no TTY, NIF/host mismatch), or the entry point
itself raises.
A no-op unless running inside a burrito-wrapped binary. Callers reach
this through start_link/3, which runs it synchronously in a wrapped
binary and asynchronously otherwise. A --version flag anywhere in
argv prints name version and exits 0 without a TTY; it first forces
the NIF dlopen, so a precompiled-NIF/host mismatch fails loudly
rather than silently exiting 0.
Options
:name(required) — the binary's name, used in--versionoutput and error messages.:version— the version string--versionprints.:halt— 1-arity function invoked with the exit code instead ofSystem.halt/1; exists for tests and embedders. Defaults toSystem.halt/1rather thanSystem.stop/1because the TUI runs during application startup — a gracefulSystem.stop/1would wait on the same still-starting application and deadlock.
Supervised entry point for a burrito-wrapped TUI — the scaffolded CLI's
start_link/1 delegates here.
Inside a wrapped binary (__BURRITO set) the TUI runs synchronously
in the calling process, so a supervised child's start_link blocks OTP
boot until the TUI exits — see the moduledoc for why an async task would
lose the race against Burrito's start_cli halt. main/3 stops the VM
when the TUI exits, so this never returns in a wrapped binary.
Outside a wrapped binary (a consumer's mix test / iex -S mix) it
starts an async, no-op task, so the supervised child never takes over
the session. Options are the same as main/3.
@spec verify_linux_nif( Mix.Release.t(), {atom(), String.t()} ) :: Mix.Release.t()
Release step that fails the build when the linux burrito target bundles a glibc NIF.
Burrito's linux wrapper runs a musl runtime, so the linux target must
bundle the musl NIF (TARGET_ABI=musl at mix release time) — a glibc
.so cannot load there and the shipped binary would hang at NIF load
on every end-user machine. Wire it between :assemble and
Burrito.wrap/1:
steps: [:assemble, &ExRatatui.Burrito.verify_linux_nif/1, &Burrito.wrap/1]A no-op unless BURRITO_TARGET=linux. Detection is a byte scan of the
assembled NIF: glibc-linked ELFs embed libc.so.6 and GLIBC_ version
references, musl ones reference libc.so alone.
Only the NIF this build actually loads is scanned — the single path
handed to :erlang.load_nif/2, which load_from names. priv/native
is a junk drawer otherwise: rustler_precompiled never evicts the
artifacts of earlier versions or ABIs, and a path dependency carries
the whole directory into the release, so scanning every .so there
fails on stale glibc siblings that no runtime ever opens.