ExRatatui.Burrito (ExRatatui v0.13.0)

Copy Markdown View Source

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

main(tui_module, argv, opts)

@spec main(module(), [String.t()], keyword()) :: :ok

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 --version output and error messages.
  • :version — the version string --version prints.
  • :halt — 1-arity function invoked with the exit code instead of System.halt/1; exists for tests and embedders. Defaults to System.halt/1 rather than System.stop/1 because the TUI runs during application startup — a graceful System.stop/1 would wait on the same still-starting application and deadlock.

start_link(tui_module, argv, opts)

@spec start_link(module(), [String.t()], keyword()) :: {:ok, pid()} | :ignore

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.

verify_linux_nif(release, load_from \\ ExRatatui.Native.load_from())

@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.