diff --git a/.cargo/config.toml b/.cargo/config.toml
new file mode 100644
index 000000000..024cc9b58
--- /dev/null
+++ b/.cargo/config.toml
@@ -0,0 +1,24 @@
+# Oak Video Editor - Non-Linear Video Editor
+# Copyright (C) 2026 Oak Team
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+# The project FFmpeg (built by tooling/ffmpeg/build-ffmpeg.sh into
+# .cache/ffmpeg) is the only supported FFmpeg: ffmpeg-sys-next reads
+# FFMPEG_DIR at build-script time, which cannot come from a .env file —
+# a relative [env] entry here is the only machine-agnostic way to set
+# it. Run tooling/ffmpeg/build-ffmpeg.sh once before the first build.
+
+[env]
+FFMPEG_DIR = { value = ".cache/ffmpeg", relative = true }
diff --git a/build.rs b/build.rs
index 4f58758a1..d42e1e376 100644
--- a/build.rs
+++ b/build.rs
@@ -30,5 +30,11 @@ fn main() {
let os = std::env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();
if os == "macos" {
println!("cargo:rustc-link-lib=framework=IOSurface");
+ // The static FFmpeg's external codec libraries pull in `-lz`, and
+ // on some machines that resolves to a package-manager copy whose
+ // install name is @rpath/libz.1.dylib; without an LC_RPATH entry
+ // the binary dies at launch ("Library not loaded"). Map the rpath
+ // at the real system library.
+ println!("cargo:rustc-link-arg=-Wl,-rpath,/usr/lib");
}
}
diff --git a/crates/oak-cli/build.rs b/crates/oak-cli/build.rs
new file mode 100644
index 000000000..6f0ed76a7
--- /dev/null
+++ b/crates/oak-cli/build.rs
@@ -0,0 +1,28 @@
+// Oak Video Editor - Non-Linear Video Editor
+// Copyright (C) 2026 Oak Team
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+//! Build-time link configuration.
+
+fn main() {
+ if std::env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("macos") {
+ // The static FFmpeg's external codec libraries pull in `-lz`, and
+ // on some machines that resolves to a package-manager copy whose
+ // install name is @rpath/libz.1.dylib; without an LC_RPATH entry
+ // the binary dies at launch ("Library not loaded"). Map the rpath
+ // at the real system library.
+ println!("cargo:rustc-link-arg=-Wl,-rpath,/usr/lib");
+ }
+}
diff --git a/crates/oak-worker/build.rs b/crates/oak-worker/build.rs
new file mode 100644
index 000000000..6f0ed76a7
--- /dev/null
+++ b/crates/oak-worker/build.rs
@@ -0,0 +1,28 @@
+// Oak Video Editor - Non-Linear Video Editor
+// Copyright (C) 2026 Oak Team
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+//! Build-time link configuration.
+
+fn main() {
+ if std::env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("macos") {
+ // The static FFmpeg's external codec libraries pull in `-lz`, and
+ // on some machines that resolves to a package-manager copy whose
+ // install name is @rpath/libz.1.dylib; without an LC_RPATH entry
+ // the binary dies at launch ("Library not loaded"). Map the rpath
+ // at the real system library.
+ println!("cargo:rustc-link-arg=-Wl,-rpath,/usr/lib");
+ }
+}
diff --git a/crates/oakffmpeg-link/build.rs b/crates/oakffmpeg-link/build.rs
index bd6fda6a1..5ffc1e68f 100644
--- a/crates/oakffmpeg-link/build.rs
+++ b/crates/oakffmpeg-link/build.rs
@@ -90,6 +90,14 @@ fn main() {
);
}
+ // System libraries (z, m, bz2, iconv) must come from the OS, not from
+ // a package manager's keg: Homebrew's zlib carries an @rpath install
+ // name, and linking it without an rpath entry breaks the binary at
+ // launch (dyld: Library not loaded: @rpath/libz.1.dylib). Put /usr/lib
+ // first in the search order so -lz resolves to the system copy.
+ #[cfg(target_os = "macos")]
+ println!("cargo:rustc-link-search=native=/usr/lib");
+
for token in String::from_utf8_lossy(&output.stdout).split_whitespace() {
if let Some(path) = token.strip_prefix("-L") {
println!("cargo:rustc-link-search=native={path}");
diff --git a/docs/build.md b/docs/build.md
index cf9c4004c..f5eeb3768 100644
--- a/docs/build.md
+++ b/docs/build.md
@@ -23,10 +23,14 @@ This document describes how to build Oak Video Editor from source on Windows, Li
> ```sh
> tooling/install-deps.sh # Homebrew / MSYS2 UCRT64 / Debian / Fedora / Arch
> tooling/ffmpeg/build-ffmpeg.sh # clones release/8.0, installs into .cache/ffmpeg
-> export FFMPEG_DIR="$(pwd)/.cache/ffmpeg"
> cargo build
> ```
>
+> `FFMPEG_DIR` no longer needs exporting: the committed
+> `.cargo/config.toml` sets it relative to the workspace root (the
+> ffmpeg-sys-next build script cannot read `.env` files — this is the
+> only machine-agnostic way).
+>
> External libraries are probed with `pkg-config` and silently skipped
> when missing. `FFMPEG_DIR` is mandatory (the `oakffmpeg-link` build
> script panics without it): silently binding a system FFmpeg risks
diff --git a/docs/zh/plans/riir/M14-direct-rlib.md b/docs/zh/plans/riir/M14-direct-rlib.md
index ca24ea969..49e1a9bbf 100644
--- a/docs/zh/plans/riir/M14-direct-rlib.md
+++ b/docs/zh/plans/riir/M14-direct-rlib.md
@@ -1,4 +1,4 @@
-# M14:前端绕过 facade 直链 rlib(纯 Rust ABI)
+ # M14:前端绕过 facade 直链 rlib(纯 Rust ABI)
> 前置:单库化(single-lib.md)+ 模块 bridge/ffi 清除已完成;
> liboakengine.dylib 的 C ABI 冻结,专供插件/外部消费者。