Rust bindings for the V8 JavaScript engine
V8 Version: 15.2.124.1
Provide high quality Rust bindings to V8's C++ API. The API should match the original API as closely as possible.
Do not introduce additional call overhead. (For example, previous attempts at Rust V8 bindings forced the use of Persistent handles.)
Do not rely on a binary libv8.a built outside of cargo. V8 is a very large
project (over 600,000 lines of C++) which often takes 30 minutes to compile.
Furthermore, V8 relies on Chromium's bespoke build system (gn + ninja) which
is not easy to use outside of Chromium. For this reason many attempts to bind
to V8 rely on pre-built binaries that are built separately from the binding
itself. While this is simple, it makes upgrading V8 difficult, it makes CI
difficult, it makes producing builds with different configurations difficult,
and it is a security concern since binary blobs can hide malicious code. For
this reason we believe it is imperative to build V8 from source code during
"cargo build".
Publish the crate on crates.io and allow docs.rs to generate documentation. Due to the complexity and size of V8's build, this is nontrivial. For example the crate size must be kept under 10 MiB in order to publish.
Rusty V8's major version aligns with Chrome's major version, which corresponds
to a specific V8 release. For example, Rusty V8 129.0.0 maps to Chrome
129.x.y.z, which uses V8 12.9.a.b. While the minor and patch numbers between
Chrome and V8 may differ, Rusty V8 will follow Chrome's release schedule, with a
new major version every 4 weeks.
As a Rust crate, Rusty V8 follows semantic versioning (semver) and will not introduce breaking changes within a major version. However, major version bumps will occur regularly to stay in sync with Chrome's release cycle.
V8 is very large and takes a long time to compile. Many users will prefer to use a prebuilt version of V8. We publish static libs for every version of rusty v8 on Github.
Binary builds are the default: cargo build will initiate a download from
github to get the static lib. To build V8 from source instead, set the
V8_FROM_SOURCE environment variable to 1 (true and yes also work). Any
other value, or leaving it unset, uses the prebuilt lib.
When making changes to rusty_v8 itself, it should be tested by build from source. The CI always builds from source.
V8_FORCE_DEBUG environment variableBy default rusty_v8 will link against release builds of v8, if you want to
use a debug build of v8 set V8_FORCE_DEBUG=true.
We default to release builds of v8 due to performance & CI reasons in deno.
RUSTY_V8_MIRROR environment variableTells the build script where to get binary builds from. Understands http://
and https:// URLs, and file paths. The default is
https://github.com/denoland/rusty_v8/releases.
For every artifact (the static lib and the generated src_binding file), the
build script tries an ordered list of locations and uses the first one that
works:
RUSTY_V8_ARCHIVE (static lib) or RUSTY_V8_SRC_BINDING_URL (binding
file), if set; either short-circuits everything else for its artifact.RUSTY_V8_MIRROR is set. A plain base is expanded to
<base>/<tag>/<file>; a value containing { placeholders is treated as a
full URL template (see below).<base>/<file>, so a
directory of downloaded artifacts works without tag subdirectories.https://github.com/denoland/rusty_v8/releases/download/<tag>/<file>.
With a mirror configured this is only tried when
RUSTY_V8_MIRROR_FALLBACK=1 is set: a mirror fails closed by default and
never silently reaches the network.If every candidate fails, the build script panics with the full list of URLs it tried.
<tag> defaults to v<version> (the crate version). Set
RUSTY_V8_MIRROR_TAG to override it; the value is used verbatim (no v is
prepended), so RUSTY_V8_MIRROR_TAG=v152.0.0 cargo build fetches the
artifacts of the last published release when building a checkout whose
Cargo.toml version is unpublished.
If the RUSTY_V8_MIRROR value contains a { placeholder, the whole value is
used as a URL template instead of a base. Supported placeholders: {tag},
{version} (no v prefix), {target}, {profile} (release/debug),
{features} (e.g. _ptrcomp), and {file} (the full artifact filename).
For example:
export RUSTY_V8_MIRROR=https://example.com/rusty_v8/{version}/{file}
Set RUSTY_V8_MIRROR_FALLBACK=1 to fall back to the upstream GitHub release
when the mirror is missing an artifact, e.g. for partially populated caches.
File-based mirrors are good for using cached downloads. First, point the environment variable to a suitable location:
# you might want to add this to your .bashrc
$ export RUSTY_V8_MIRROR=$HOME/.cache/rusty_v8
Then populate the cache:
#!/bin/bash
# see https://github.com/denoland/rusty_v8/releases
for REL in v152.1.0 v152.0.0; do
mkdir -p $RUSTY_V8_MIRROR/$REL
for FILE in \
librusty_v8_release_x86_64-unknown-linux-gnu.a.gz \
src_binding_release_x86_64-unknown-linux-gnu.rs \
; do
if [ ! -f $RUSTY_V8_MIRROR/$REL/$FILE ]; then
wget -O $RUSTY_V8_MIRROR/$REL/$FILE \
https://github.com/denoland/rusty_v8/releases/download/$REL/$FILE
fi
done
done
~/.cargo/.rusty_v8 download cacheBefore downloading an artifact, the build script looks for a copy in the
.rusty_v8 directory inside your Cargo home (usually ~/.cargo/.rusty_v8).
Entries are keyed on the release tag plus the artifact filename, with every
non-alphanumeric character replaced by _ — for example
v152.1.0/librusty_v8_release_x86_64-unknown-linux-gnu.a.gz becomes
v152_1_0_librusty_v8_release_x86_64_unknown_linux_gnu_a_gz. The escaped
full source URL, the key used by older versions of the build script, is
still checked as a fallback, so existing caches keep working.
Because the key does not include the source, a cache entry populated for one
mirror also satisfies a build configured for a different mirror (or for the
upstream release) under the same tag. If you need the archive bytes
themselves verified, pin them with RUSTY_V8_ARCHIVE_SHA256 (below).
RUSTY_V8_ARCHIVE environment variableTell the build script to use a specific v8 library. This can be an URL or a path. This is useful when you have a prebuilt archive somewhere:
export RUSTY_V8_ARCHIVE=/path/to/custom_archive.a
cargo build
The value may also name a directory, in which case the expected artifact
filename (e.g. librusty_v8_release_x86_64-unknown-linux-gnu.a.gz, gzipped
or plain) is looked up inside it. A directory is also the authoritative
source for the generated src_binding file: it is never fetched from the
mirror or the upstream release, so an offline setup that configured only the
directory never reaches the network. If the directory lacks the binding, a
usable binding left on disk by a previous build is reused with a warning;
otherwise the build fails:
export RUSTY_V8_ARCHIVE=/path/to/downloaded/artifacts
cargo build
Set RUSTY_V8_ARCHIVE_SHA256 to the SHA-256 of the archive to pin its
content. A cached or previously downloaded archive that does not match is
re-fetched, and the build fails if the fresh download does not match either.
The pin covers the archive bytes as fetched, i.e. what sha256sum reports
on the .gz release asset (or on the plain file when the archive is not
gzipped). Independently of the pin, the build script records the SHA-256 of
every downloaded artifact and re-fetches it if the file on disk no longer
matches.
RUSTY_V8_SRC_BINDING_PATH and RUSTY_V8_SRC_BINDING_URL environment variablesThe build also needs a generated src_binding_..._<target>.rs file, published
alongside the static library. RUSTY_V8_SRC_BINDING_PATH points the build at
a local binding file that is used directly, with no download at all.
RUSTY_V8_SRC_BINDING_URL instead gives a URL or path to fetch the binding
from, mirroring what RUSTY_V8_ARCHIVE does for the static library. If both
are set, RUSTY_V8_SRC_BINDING_PATH wins.
RUSTY_V8_SKIP_DOWNLOAD environment variableSet RUSTY_V8_SKIP_DOWNLOAD=1 to skip downloading the prebuilt static
library. The small generated binding file is still fetched, so cargo check
and rust-analyzer work without the (large) prebuilt artifact. Producing a
binary still requires the static library: cargo build fails at link time
until the crate is built again with the variable unset.
This variable takes precedence over RUSTY_V8_ARCHIVE and RUSTY_V8_MIRROR
(the static library is not fetched from anywhere, not even from a local
archive), and it has no effect on V8_FROM_SOURCE=1 builds. If the binding
file cannot be fetched (for example, the configured mirror does not carry it)
but a previously downloaded binding exists on disk, that file is reused with
a warning instead of failing the build.
Use V8_FROM_SOURCE=1 cargo build -vv to build the crate completely from
source.
The build scripts require Python 3 to be available as python3 in your PATH.
If you want to specify the exact binary of Python to use, you should use the
PYTHON environment variable.
The build also requires curl to be installed on your system.
For linux builds: glib-2.0 development files need to be installed such that
pkg-config can find them. On Ubuntu, run sudo apt install libglib2.0-dev to
install them.
Additionally, building from source requires libclang 21.1+ for bindgen:
sudo apt install libclang-21-dev
export LIBCLANG_PATH=/usr/lib/llvm-21/lib
Linux cross-builds normally discover Clang's builtin headers and the target libc headers from the host toolchain. For hermetic toolchains where those files are not installed in host search paths, set the explicit bindgen inputs:
export LIBCLANG_PATH=/path/to/libclang/lib
export RUSTY_V8_BINDGEN_RESOURCE_DIR=/path/to/lib/clang/21
export RUSTY_V8_GLIBC_PREFIX=/path/to/aarch64-linux-gnu
V8_FROM_SOURCE=1 cargo build -vv --target aarch64-unknown-linux-gnu
RUSTY_V8_BINDGEN_RESOURCE_DIR takes the directory printed by
clang -print-resource-dir. RUSTY_V8_GLIBC_PREFIX takes a GNU target prefix
whose include child contains the target libc headers. Musl cross-builds use
RUSTY_V8_MUSL_SYSROOT instead; it is passed to Clang with --sysroot.
For Windows builds: the 64-bit toolchain needs to be used. 32-bit targets are
not supported. The default source build downloads Chromium's pinned libclang
automatically. If $CLANG_BASE_PATH is set to a custom LLVM installation,
$LIBCLANG_PATH must point to the directory containing libclang.dll.
The tools/win submodule is skipped because its standalone mirror is
unreliable, so source builds must populate its pinned debugger visualizers:
mkdir -p tools/win
curl -fL https://chromium.googlesource.com/chromium/src/tools/win/+archive/faefd1b6fa9eeb033ad6fe60368ccb9bf908cbd0.tar.gz |
tar -xz -C tools/win
For Mac builds: You'll need Xcode and Xcode CLT installed. Recent macOS versions
will also require you to pass PYTHON=python3 because macOS no longer ships with
python simlinked to Python 3.
For Android builds: You'll need to cross compile from a x86_64 host to the aarch64 or x64 android. You can use the following commands:
rustup target add aarch64-linux-android # or x86_64-linux-android
V8_FROM_SOURCE=1 cargo build -vv --target aarch64-linux-android
# or with cross
docker build --build-arg CROSS_BASE_IMAGE=ghcr.io/cross-rs/aarch64-linux-android:0.2.5 -t cross-rusty_v8:aarch64-linux-android .
V8_FROM_SOURCE=1 cross
Not Support MarkAsUndetectable API?
Every OwnedIsolate leaks its IsolateLiveness cell (~160 bytes per isolate, never freed)
v8_enable_sandbox source build fails from published crate because required inputs are absent
Build error by Google's rotten meta-build system: BUILD.gn:2833:9: Assignment had no effect.
Expose a way to pin the ValueSerializer format version (workerd's SetWriteVersion)
Can't compile on FreeBSD
v8::scope documentation is private and not visible in generated docs