diff --git a/scripts/build/buildOptionsRs.ts b/scripts/build/buildOptionsRs.ts
index a24fc16829b9..6225923a15f9 100644
--- a/scripts/build/buildOptionsRs.ts
+++ b/scripts/build/buildOptionsRs.ts
@@ -69,7 +69,6 @@ export function generateBuildOptionsRs(cfg: Config): string {
"pub const ENABLE_TINYCC: bool = !cfg!(any(",
` all(windows, target_arch = "aarch64"),`,
` target_os = "android",`,
- ` target_os = "freebsd",`,
"));",
"",
];
diff --git a/scripts/build/config.ts b/scripts/build/config.ts
index 37e056083fbc..6d68df459c2f 100644
--- a/scripts/build/config.ts
+++ b/scripts/build/config.ts
@@ -849,10 +849,9 @@ export function resolveConfig(partial: PartialConfig, toolchain: Toolchain): Con
// failure is loud ("cannot find -l:libatomic.a") and the fix is obvious.
const staticLibatomic = partial.staticLibatomic ?? true;
- // TinyCC: off on Windows ARM64 (not supported), Android (no upstream
- // bionic support; FFI cc() falls back to dlopen-only), and FreeBSD
- // (oven-sh/tinycc has no FreeBSD target).
- const tinycc = partial.tinycc ?? !((windows && arm64) || abi === "android" || freebsd);
+ // TinyCC: off on Windows ARM64 (not supported) and Android (no upstream
+ // bionic support; FFI cc() falls back to dlopen-only).
+ const tinycc = partial.tinycc ?? !((windows && arm64) || abi === "android");
const valgrind = partial.valgrind ?? false;
const fuzzilli = partial.fuzzilli ?? false;
diff --git a/scripts/build/deps/tinycc.ts b/scripts/build/deps/tinycc.ts
index 499a83bc16df..dbd752d51c4b 100644
--- a/scripts/build/deps/tinycc.ts
+++ b/scripts/build/deps/tinycc.ts
@@ -58,6 +58,7 @@ export const tinycc: Dependency = {
// driver) so we leave it at the default.
}
if (cfg.windows) defines.CONFIG_WIN32 = true;
+ if (cfg.freebsd) defines.TARGETOS_FreeBSD = true;
const spec: DirectBuild = {
kind: "direct",
diff --git a/scripts/build/tools.ts b/scripts/build/tools.ts
index ff216313129c..f6630864278e 100644
--- a/scripts/build/tools.ts
+++ b/scripts/build/tools.ts
@@ -303,7 +303,7 @@ function llvmSearchPaths(os: OS, arch: Arch): string[] {
paths.push("C:\\Program Files\\LLVM\\bin");
}
- if (os === "linux" || os === "darwin") {
+ if (os === "linux" || os === "darwin" || os === "freebsd") {
paths.push("/usr/lib/llvm/bin");
// Debian/Ubuntu-style suffixed paths
paths.push(`/usr/lib/llvm-${LLVM_MAJOR}.${LLVM_MINOR}.0/bin`);
@@ -312,6 +312,12 @@ function llvmSearchPaths(os: OS, arch: Arch): string[] {
paths.push(`/usr/lib/llvm${LLVM_MAJOR}/bin`);
}
+ if (os === "freebsd") {
+ // FreeBSD ports install to /usr/local with unsuffixed version numbers
+ paths.push(`/usr/local/llvm${LLVM_MAJOR}/bin`);
+ paths.push("/usr/local/bin");
+ }
+
return paths;
}
@@ -325,6 +331,7 @@ function llvmNameVariants(name: string): string[] {
`${name}-${LLVM_MAJOR}.${LLVM_MINOR}.0`,
`${name}-${LLVM_MAJOR}.${LLVM_MINOR}`,
`${name}-${LLVM_MAJOR}`,
+ `${name}${LLVM_MAJOR}`, // FreeBSD ports: clang21, lld21
];
}
@@ -482,7 +489,7 @@ export function resolveLlvmToolchain(
let ld: string;
if (msvcTarget) {
ld = findLlvmTool("lld-link", paths, os, { checkVersion: false, required: true })?.path ?? "";
- } else if (os === "linux") {
+ } else if (os === "linux" || os === "freebsd") {
ld = findLlvmTool("ld.lld", paths, os, { checkVersion: true, required: true })?.path ?? "";
} else {
ld = ""; // darwin: unused
@@ -502,7 +509,7 @@ export function resolveLlvmToolchain(
// Mach-O, so darwin cross-compiles need it (resolveConfig swaps it in).
let strip: string;
let llvmStrip: string | undefined;
- if (os === "linux") {
+ if (os === "linux" || os === "freebsd") {
strip = findTool({ names: ["strip"], required: true, hint: "Install binutils for your distro" })?.path ?? "";
llvmStrip = findLlvmTool("llvm-strip", paths, os, { checkVersion: false, required: false })?.path;
} else {
diff --git a/src/runtime/ffi/mod.rs b/src/runtime/ffi/mod.rs
index 940a6a5695bf..9525df2e33bd 100644
--- a/src/runtime/ffi/mod.rs
+++ b/src/runtime/ffi/mod.rs
@@ -122,7 +122,6 @@ mod TCC {
// TODO(port): move to _sys
#[cfg(not(any(
target_os = "android",
- target_os = "freebsd",
all(windows, target_arch = "aarch64")
)))]
unsafe extern "C" {
@@ -130,7 +129,6 @@ mod TCC {
}
#[cfg(any(
target_os = "android",
- target_os = "freebsd",
all(windows, target_arch = "aarch64")
))]
pub(super) unsafe fn tcc_delete(_s: *mut State) {
diff --git a/src/tcc_sys/tcc.rs b/src/tcc_sys/tcc.rs
index 66257f510d1f..2808eb374945 100644
--- a/src/tcc_sys/tcc.rs
+++ b/src/tcc_sys/tcc.rs
@@ -14,8 +14,8 @@ pub type TCCErrorFunc = Option = unsafe extern "C" fn(ctx: *mut Ctx, msg: *const c_char);
// `libtcc.a` is only built where `cfg.tinycc` is true (`scripts/build/config.ts`):
-// not Windows/aarch64 (TinyCC has no aarch64-pe-coff backend), not Android, not
-// FreeBSD (the vendored fork doesn't support those targets). On those platforms
+// not Windows/aarch64 (TinyCC has no aarch64-pe-coff backend), not Android.
+// On those platforms
// these `extern "C"` decls would be undefined at link. Zig's `comptime
// !Environment.enable_tinycc` early-returns in `ffi.zig` keep the *Zig*
// callers off the analysis graph, so the Zig externs never get emitted; Rust
@@ -31,13 +31,13 @@ pub type ErrorFunc = unsafe extern "C" fn(ctx: *mut Ctx, msg: *const c_char
// Keep this predicate in sync with `cfg.tinycc` in `scripts/build/config.ts`.
macro_rules! tcc_externs {
($($(#[$attr:meta])* fn $name:ident($($arg:ident: $ty:ty),* $(,)?) $(-> $ret:ty)?;)*) => {
- #[cfg(not(any(target_os = "android", target_os = "freebsd", all(windows, target_arch = "aarch64"))))]
+ #[cfg(not(any(target_os = "android", all(windows, target_arch = "aarch64"))))]
// TODO(port): move to tcc_sys (already in *_sys crate — verify crate layout)
unsafe extern "C" {
$($(#[$attr])* fn $name($($arg: $ty),*) $(-> $ret)?;)*
}
$(
- #[cfg(any(target_os = "android", target_os = "freebsd", all(windows, target_arch = "aarch64")))]
+ #[cfg(any(target_os = "android", all(windows, target_arch = "aarch64")))]
#[allow(unused_variables, clippy::missing_safety_doc)]
unsafe extern "C" fn $name($($arg: $ty),*) $(-> $ret)? {
unreachable!(concat!(