Skip to content

feat(fff-bun): recognise FreeBSD as a platform - #824

Open
apbodrov wants to merge 1 commit into
dmtrKovalenko:mainfrom
apbodrov:freebsd-support
Open

feat(fff-bun): recognise FreeBSD as a platform#824
apbodrov wants to merge 1 commit into
dmtrKovalenko:mainfrom
apbodrov:freebsd-support

Conversation

@apbodrov

@apbodrov apbodrov commented Aug 26, 2026

Copy link
Copy Markdown

Summary

Two small changes so @ff-labs/fff-bun can be used on FreeBSD. The Rust side needs nothingcargo build --release -p fff-c builds crates/fff-c into a working libfff_c.so on FreeBSD exactly as-is. This is purely the JS platform plumbing.

The two blockers

1. package.json "os" omits freebsd, so bun/npm skip installing the package altogether. A consumer importing it gets:

error: Cannot find module '@ff-labs/fff-bun' from '.../fff.bun.ts'

2. getTriple() throws before any lookup can happen. It is called from getNpmPackageName(), which resolveFromNpmPackage() invokes outside its try/catch:

function resolveFromNpmPackage(): string | null {
  const packageName = getNpmPackageName();   // <-- throws here on freebsd

  try {
    // ...
  } catch {
    // Package not installed - this is expected on unsupported platforms
  }
  return null;
}

So Unsupported platform: freebsd escapes findBinary() entirely, instead of degrading to "no prebuilt for this platform, fall through to the dev build". The dev-build fallback already exists and works fine — it is just never reached.

About the unpublished package names

The two triples added to TRIPLE_TO_NPM_PACKAGE point at @ff-labs/fff-bin-freebsd-{x64,arm64}, which do not exist on npm. This is deliberate and harmless: resolveFromNpmPackage() catches the resolution failure and findBinary() moves on to the locally built library. If you ever publish those packages the mapping goes live with no further code change.

The alternative — wrapping getNpmPackageName() in a try/catch at the call site — would also work and would not name unpublished packages. Happy to switch to that if you prefer it; I went with the mapping because it keeps the "which platforms exist" knowledge in one table.

Verification

FreeBSD 15.1-RC2, amd64, bun 1.4.0, rustc 1.96.1:

  • cargo build --release -p fff-ctarget/release/libfff_c.so, unpatched.
  • findBinary() resolves it, dlopen succeeds through bun:ffi.
  • FileFinder.create({ basePath }) + waitForScan() + fileSearch() return real results — 100 hits over a live tree, with relativePath, fileName, gitStatus, size, modified all populated correctly.

Not covered

packages/fff-node/src/platform.ts has the same Unsupported platform throw at line 24 and presumably needs the same treatment, but I only exercised the bun package, so I left it alone rather than ship an untested change. Say the word and I will mirror it.

aarch64-unknown-freebsd is mapped but was not built or tested — drop that line if you would rather not carry an unverified target.

Context

This came from getting a Bun-based coding agent running on FreeBSD; fff was the first of three native dependencies to need attention, and the only one where the native code itself was already fine.

Summary by CodeRabbit

  • New Features
    • Added support for FreeBSD on x86_64 and ARM64 systems.
    • Added corresponding FreeBSD binary packages for installation.

On FreeBSD the package is unusable for two independent reasons, neither
of them in the Rust code — `cargo build --release -p fff-c` builds
crates/fff-c into a working libfff_c.so on FreeBSD as-is.

1. package.json's "os" list omits freebsd, so bun and npm skip
   installing the package entirely. A consumer importing it just gets
   "Cannot find module '@ff-labs/fff-bun'".

2. getTriple() throws "Unsupported platform: freebsd". It is reached
   from getNpmPackageName(), which resolveFromNpmPackage() calls
   *outside* its try/catch, so the throw escapes findBinary() instead of
   degrading to "no prebuilt available, fall back to the dev build".
   The fallback path exists and works; it is simply never reached.

The two triples added to TRIPLE_TO_NPM_PACKAGE point at
@ff-labs/fff-bin-freebsd-{x64,arm64}, which are not published. That is
harmless: resolveFromNpmPackage() swallows the resolution failure and
findBinary() continues to the locally built library. Publishing those
packages later makes the mapping live with no further code change.

Verified on FreeBSD 15.1 (amd64, bun 1.4.0, rustc 1.96.1): libfff_c.so
loads through bun:ffi, and FileFinder.create() + fileSearch() return
real results with git status and file metadata over a live tree.

aarch64-unknown-freebsd is mapped but was not built or tested.
@coderabbitai

coderabbitai Bot commented Aug 26, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b8884f33-c316-4fb0-85f1-fbb3f18eb8e3

📥 Commits

Reviewing files that changed from the base of the PR and between c642d07 and 6d5e450.

📒 Files selected for processing (2)
  • packages/fff-bun/package.json
  • packages/fff-bun/src/platform.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

Changes

FreeBSD support

Layer / File(s) Summary
Add FreeBSD runtime and binary targets
packages/fff-bun/package.json, packages/fff-bun/src/platform.ts
The package allowlist accepts FreeBSD. Runtime detection maps FreeBSD to unknown-freebsd. x86_64 and ARM64 FreeBSD targets map to npm binary packages.

Estimated code review effort: 2 (Simple) | ~5 minutes

Merge Risk: ⚪ Minimal · up to 6d5e4

The change adds FreeBSD support for the intended x64 and arm64 platforms while keeping unsupported arm installs excluded. No actionable merge-blocking risk remains.

Suggested reviewers: dmtrkovalenko

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding FreeBSD platform support to fff-bun.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 1 files. (1 skipped: 1 …
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 1 files. (1 skipped: 1 unsupported.)

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@apbodrov

Copy link
Copy Markdown
Author

aarch64 update — the PR said aarch64-unknown-freebsd was mapped but not built. It has been built now.

FreeBSD 14.4-RELEASE-p5 arm64, rustc/cargo 1.94.0, this branch checked out as-is:

cargo build --release -p fff-c
    Finished `release` profile [optimized] target(s) in 61m 07s     (qemu TCG, no hardware virt)

target/release/libfff_c.so
  ELF 64-bit LSB shared object, ARM aarch64, version 1 (FreeBSD)
  91 exported text symbols

And it loads — dlopen(..., RTLD_NOW) plus dlsym on two of the C entry points:

DLOPEN_OK create=0x824a65d0 destroy=0x824a8eb0

No Rust changes were needed for arm64, same as for amd64 — crates/fff-c builds clean on both as it stands.

Being precise about what this does and does not show: this patch is JS-only (platform.ts + package.json), so what arm64 adds here is mainly that the native library the FreeBSD triples point at really does build on that architecture. The FFI-level exercise (FileFinder.create() + fileSearch() returning real results) was run on amd64, not arm64 — bun is not installed on the arm64 box.

The 61 minutes is emulation overhead, not something about the code: this is qemu emulating aarch64 on an x86 host, roughly 12x slower than native by wall clock.

@dmtrKovalenko

Copy link
Copy Markdown
Owner

@gustav-fff do we provide precompuled binaries for FreeBSD? I do not remember but I doubt it

@gustav-fff

Copy link
Copy Markdown
Collaborator

[triage-bot] DIRECTED: No. There are zero FreeBSD prebuilts, in any channel.

  • C FFI matrix .github/workflows/release.yaml:172-233 — 9 legs, none freebsd: linux gnu/musl x64+arm64, android-arm64, darwin x64/arm64, windows x64/arm64.
  • nvim matrix :42-84, MCP matrix :328-358 — same set, no freebsd.
  • No packages/fff-bin-freebsd-* dir. scripts/set-npm-version.mjs:44-50 derives the published platform list from readdirSync("packages") filtered on fff-bin-, so a leg that does not exist as a directory is never published.
  • Registry confirms:
GET https://registry.npmjs.org/@ff-labs/fff-bin-freebsd-x64    -> 404
GET https://registry.npmjs.org/@ff-labs/fff-bin-freebsd-arm64  -> 404

So platform.ts:115-116 are dead entries today. Harmless as the PR says, but one caveat it understates: with no prebuilt, findBinary() (packages/fff-bun/src/download.ts:113) falls to getDevBinaryPath(), which probes <pkg>/../../target/{release,debug}. From a git checkout that is the repo target dir — that is what was exercised. From node_modules/@ff-labs/fff-bun it resolves node_modules/target/release/libfff_c.so, which nothing creates. Net effect for an npm-installed FreeBSD consumer: error moves from Cannot find module to findBinary() === null. Still strictly better (package installs, source build works), but not "FreeBSD supported".

Making it real needs packages/fff-bin-freebsd-{x64,arm64} + release legs, and there is no GitHub-hosted FreeBSD runner — means a VM action or a FreeBSD sysroot for cargo-zigbuild. Your call if that is worth carrying.
Honk-Honk 🪿

@apbodrov

Copy link
Copy Markdown
Author

@gustav-fff is right, and the caveat is a fair hit on my wording — I checked it against the code rather than taking it on faith:

  • getDevBinaryPath() (packages/fff-bun/src/download.ts:80-96) builds its candidates from join(getPackageDir(), "..", ".."). From a git checkout that is the repo root, so target/release/libfff_c.so is found — which is exactly the path my verification exercised, and I should have said so explicitly.
  • From node_modules/@ff-labs/fff-bun the same expression resolves node_modules/target/release/libfff_c.so, which nothing produces.
  • There is no env override to fall back on: grep -n "process.env" packages/fff-bun/src/*.ts returns nothing.

So for an npm-installed FreeBSD consumer this PR moves the failure from "package refuses to install / cannot find module" to "package installs, findBinary() returns null". Better, and honest to call it that rather than "FreeBSD supported".

Two ways to close the remaining gap, both small enough that I am happy to do either — your call on whether it is worth carrying:

A. An env override, no CI cost. Something like FFF_C_LIB=/path/to/libfff_c.so consulted first in findBinary(), roughly ten lines. It makes the source-build route usable from an npm install on any platform you do not ship prebuilts for, not just FreeBSD, and costs nothing on platforms that do have them. This is the one I would suggest, since it does not commit you to a release leg you then have to keep green.

B. Real prebuilts. As noted there is no GitHub-hosted FreeBSD runner. Two workable routes: vmactions/freebsd-vm, which runs steps inside a FreeBSD VM on a Linux runner and is what most FreeBSD CI in the wild uses; or cargo-zigbuild with a FreeBSD sysroot, which stays on the Linux runner and is faster but needs the sysroot pinned somewhere. Either needs packages/fff-bin-freebsd-{x64,arm64} so scripts/set-npm-version.mjs picks them up.

If you would rather not take on B, I can also just drop the two TRIPLE_TO_NPM_PACKAGE entries from this PR — they are the only part that names packages that do not exist. The os field and the getTriple() throw are the parts that actually matter, and they stand on their own.

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