Skip to content

core: add FreeBSD (x86_64, aarch64) support - #1565

Closed
samm-git wants to merge 5 commits into
anomalyco:mainfrom
samm-git:freebsd-support
Closed

samm-git wants to merge 5 commits into
anomalyco:mainfrom
samm-git:freebsd-support

Conversation

@samm-git

@samm-git samm-git commented Oct 4, 2026

Copy link
Copy Markdown

What

Adds FreeBSD (x86_64 and aarch64) as a supported platform for OpenTUI's native Zig core.

Changes

  • packages/native/build.zig: add x86_64-freebsd and aarch64-freebsd to SUPPORTED_TARGETS.
  • packages/core/src/node-asset-target.ts: accept freebsd and map it to libopentui.so.
  • packages/core/src/platform/runtime-assets.bun.ts: resolve @opentui/core-freebsd-{x64,arm64} at runtime.
  • packages/core/scripts/variants.ts: build/package the FreeBSD variants.
  • packages/core/package.json: list the FreeBSD packages as optionalDependencies.
  • packages/core/src/node-assets.test.ts: FreeBSD is now supported, so the "unsupported platform" case uses a different platform.

Why

OpenTUI currently only ships darwin/linux/win32 binaries, and the native-asset loader hard-rejects freebsd (the NodeAssetTarget union and NATIVE_FILE_NAMES don't include it). Applications that depend on OpenTUI — for example OpenCode, whose TUI is built on it — therefore can't run on FreeBSD without building the core from source and patching the loader. This makes FreeBSD a first-class target.

Testing

Zig 0.16.0 on FreeBSD 15.1, both architectures:

cd packages/native
sh scripts/prepare-zig-deps.sh
zig build -Doptimize=ReleaseFast
# -> lib/x86_64-freebsd/libopentui.so
# -> lib/aarch64-freebsd/libopentui.so

The resulting libopentui.so loads over FFI and renders correctly — verified end-to-end by running OpenCode's TUI on FreeBSD/amd64 and FreeBSD/aarch64.

Notes

  • No changes were needed inside the Zig core itself; it builds unmodified for FreeBSD once the targets are listed.
  • The node runtime path already resolves dynamically via asset.packageName, so only the bun FFI path needed an explicit branch.

Add FreeBSD as a supported platform for OpenTUI's native Zig core.

* packages/native/build.zig: add x86_64-freebsd and aarch64-freebsd
  targets so `zig build` can produce libopentui.so for FreeBSD.
* packages/core: teach the native asset loader about freebsd
  (NATIVE_FILE_NAMES + the resolveNativeLibraryPath runtime branch).
* packages/core: resolve the @opentui/core-freebsd-{x64,arm64} packages
  at runtime and list them as optionalDependencies.
* packages/core: include freebsd in scripts/variants.ts so the host
  build works and the release build packages freebsd binaries.

Tested on FreeBSD 15.1 (Zig 0.16.0) on both x86_64 and aarch64: the
native core builds with `zig build -Doptimize=ReleaseFast`, and the
resulting libopentui.so loads and renders correctly.
The native renderer thread deadlocks on FreeBSD (the TUI freezes after the
first render), so turn it off there too, matching the existing Linux guard.
@samm-git

Copy link
Copy Markdown
Author

Follow-up for OpenCode's persistent terminals: this PR enables the native-target and asset plumbing, but ghosttyVtAvailable() still returns false on FreeBSD, so @opentui/core builds the embedded-terminal/unavailable.zig stub and createEmbeddedTerminal() returns -3 ("embedded terminal support is unavailable"). OpenCode's TUI calls that when it opens a terminal pane, and the -3 becomes an uncaught error that crashes the whole app.

Adding .freebsd => true to the OS switch in ghosttyVtAvailable() fixes it:

    return switch (target.result.os.tag) {
        .linux => target.result.abi.isMusl() or target.result.abi == .gnu,
        .macos => true,
        .windows => target.result.abi == .gnu,
        .freebsd => true,
        else => false,
    };

Verified on FreeBSD 15.1/aarch64 with Zig 0.16.0: after the change zig build -Doptimize=ReleaseFast still exits 0, createEmbeddedTerminal(80, 24, 1000, &handle) returns 0 (was -3), resize works, and an OpenCode terminal pane renders normally. The vendored Ghostty already supports FreeBSD, and opentui passes i18n=false, so the Zig 0.16 trimRight/trimEnd rename doesn't apply here.

@tuaris

tuaris commented Nov 4, 2026

Copy link
Copy Markdown

Tested this approach end to end and it works. A few gaps remain between landing the source support and actually shipping @opentui/core-freebsd-*:

Missing pieces to publish the packages

  1. packages/core/scripts/native-symbols.ts - separateNativeSymbols throws Unsupported symbol platform: freebsd for the new variants. FreeBSD is ELF; folding platform === "freebsd" into the linux branch (readelf/objcopy/nm) is sufficient.
  2. .github/workflows/build-native.yml - the per-variant zip blocks, the core-native-packages copy list, the verify checks, and the upload-artifact path list are all hand-enumerated. Each needs native-freebsd-x64/native-freebsd-arm64 entries or the artifacts never reach the npm staging.
  3. .github/workflows/release.yml - same enumeration: the signed-native upload list, the symbol target loop, and EXPECTED_ASSETS 20 -> 24.

Stale hunk

renderer.ts: useThread no longer exists on main, so that part of the diff is obsolete and can be dropped.

Verified

zig build -Dtarget=x86_64-freebsd -Doptimize=ReleaseFast on current main + this branch produces lib/x86_64-freebsd/{libopentui.so,libopentui.a} with zig 0.17.0 (FreeBSD 15.1, amd64). A consumer built on this branch (the Cline CLI TUI) loads the .so over FFI and renders correctly.

Happy to push the workflow/symbol-separation half as a follow-up branch if that is easier than amending here.

# Conflicts:
#	packages/core/package.json
#	packages/core/src/renderer.ts
The source-side support is in place but the release pipeline is
hand-enumerated, so the new variants would never ship. Add the freebsd
zip blocks, npm-package copy list, verify checks, upload entries, and
symbol targets to build-native/release, and fold freebsd into the ELF
branch of separateNativeSymbols (FreeBSD shares readelf/objcopy/nm).
@tuaris

tuaris commented Nov 4, 2026

Copy link
Copy Markdown

Opened samm-git#1 against your freebsd-support branch: merges current main (resolves the two conflicts) and adds the missing publish pieces listed above. Merging it makes this PR conflict-free and CI-complete; your commits stay untouched.

main merge + CI: publish freebsd native artifacts, fix symbol separation
@simonklee

Copy link
Copy Markdown
Member

Thanks. We're keeping #1445 as the base for FreeBSD support, so I'm closing this one.

@simonklee simonklee closed this Nov 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants