Skip to content

Build from Source

losenine edited this page Oct 31, 2026 · 2 revisions

English · 中文

Build from Source

Most users never need this: pip install ruyijs already contains the runtime on Windows x64, macOS arm64 and Linux x86_64. Build from source when you change the C++ under src/. The authoritative build notes are tools/README.md (Windows, in Chinese), docs/macos.md (macOS) and docs/linux.md (Linux).

What gets built, and from where

  • JavaScript engine: the engine inside js.exe is Firefox's official SpiderMonkey, from the official Firefox source (tag FIREFOX_159_0_RELEASE). tools/build.py links the SpiderMonkey objects Firefox produces with the ruyiJS browser environment under src/ into js.exe.
  • Canvas 2D: drawn by Firefox's own Skia — the gfx/skia objects of a Firefox browser build.
  • Network: Firefox's official Necko stack (the Firefox 159 HTTP/1.1, HTTP/3 and brotli native components). It ships with the runtime package under vendor/necko-derived.
  • Third-party native libraries: vendor/compression-native (zlib-rs, brotli), vendor/image-native (dav1d, libavif, libyuv, qcms) and vendor/urlpattern-native, all taken from the official Firefox source tree. Each directory keeps its original license, and manifest.json records every file's SHA-256.

Both the engine and Necko can be compiled from the official Firefox source.

Skipping the compile

  • pip install ruyijs — the wheel bundles the same runtime.
  • Or download ruyijs-runtime-<version>-win-amd64.zip from a GitHub release (dist/js.exe, runtime libraries, Necko). Unzip it and set RUYIJS_SHELL=<unzip-dir>\dist\js.exe, or place it at the repository root (giving dist/js.exe and vendor/necko-derived); python/ruyijs finds it automatically.

The firefox/ submodule and the CI scripts

Since 0.3.0 the repository references the official Firefox source as the firefox/ submodule (mozilla-firefox/firefox at FIREFOX_159_0_RELEASE), and GitHub Actions builds every PyPI wheel from it (.github/workflows/wheels.yml). The scripts those workflows run are the shortest way to a local build:

git clone --recurse-submodules https://github.com/cutetailbud/ff-ruyijs
cd ff-ruyijs

Windows x64 (in a MozillaBuild shell) and macOS arm64 (with Python 3.12 first on PATH):

bash ci/firefox/build.sh js         # SpiderMonkey -> obj/js
bash ci/firefox/build.sh browser    # only Skia, NSS/NSPR and mozglue -> obj/firefox-parts
bash ci/firefox/build.sh toolchain  # the clang tools/build.py links with
python tools/build.py --donor-obj obj/js --toolchain-bin "$HOME/.mozbuild/clang/bin" --firefox-gfx-obj obj/firefox-parts

Windows then assembles the runtime archive (python ci/package/runtime.py) and builds the wheel with RUYIJS_BUILD_RUNTIME_ARCHIVE pointing at it. macOS also links libnecko_core.dylib (tools/build.py --necko-core-only --firefox-gfx-obj obj/firefox-parts) and libnecko_quic.dylib (tools/build_necko_quic.py library) before pip wheel .. Every step, with its environment, is in build-windows.yml and build-macos.yml.

Linux x86_64 (Ubuntu 24.04): run ci/linux/build.sh deps, donor, shell, necko, wheel and test in that order; see docs/linux.md.

The manual steps below build the same parts from a full Firefox tree. They also give you the firefox.exe that pixel comparisons use as the reference, and the xpcshell that proxies need on macOS.

Windows x64

js.exe is linked from two official Firefox build outputs plus this repository's src/:

  • SpiderMonkey standalone (--enable-project=js): every object file of the JS engine;
  • Firefox browser build: its gfx/skia object files, so Canvas 2D is drawn by Firefox's own Skia. The firefox.exe it produces is also the reference for pixel comparisons.

Both must come from the same Firefox version (currently 159).

1. Get the Firefox source

git clone https://github.com/mozilla-firefox/firefox
cd firefox
git tag --list "FIREFOX_159_0*_RELEASE"   # list the available 159 release tags
git checkout FIREFOX_159_0_RELEASE

The official repository is https://github.com/mozilla-firefox/firefox (Mozilla's official Git mirror; browse the source at https://searchfox.org/mozilla-central/).

2. Install the toolchain

Run ./mach bootstrap. It installs Firefox's toolchain — clang-cl, lld-link, Rust and the VS/Windows SDK — under %USERPROFILE%\.mozbuild.

3. Build SpiderMonkey standalone

mozconfig-js:

mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/obj-js-standalone
ac_add_options --enable-project=js
ac_add_options --enable-optimize
ac_add_options --disable-debug
ac_add_options --disable-tests
ac_add_options --enable-rust-simd
ac_add_options --target=x86_64-pc-windows-msvc
$env:MOZCONFIG = "$PWD\mozconfig-js"
./mach build

4. Build the Firefox browser

Same release configuration as the official build. mozconfig-browser:

mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/obj-release
export MOZILLA_OFFICIAL=1
export MOZ_LTO=cross
ac_add_options --target=x86_64-pc-windows-msvc
ac_add_options --enable-optimize
ac_add_options --disable-debug
ac_add_options --enable-rust-simd
ac_add_options --enable-release
ac_add_options --as-milestone=release
ac_add_options --enable-update-channel=release
$env:MOZCONFIG = "$PWD\mozconfig-browser"
./mach build

obj-release\gfx\skia\ should then hold about 80 .obj files; obj-release\dist\bin\firefox.exe is the reference browser.

5. Build js.exe

From the ruyiJS repository root:

$env:RUYIJS_DONOR_OBJ = "<firefox>\obj-js-standalone"
$env:RUYIJS_FIREFOX_GFX_OBJ = "<firefox>\obj-release"
python -B tools/build.py `
  --toolchain-bin "$env:USERPROFILE\.mozbuild\clang\bin" `
  --vs-root "$env:USERPROFILE\.mozbuild\vs"
  • The gfx/skia/*.obj files are archived as firefox_skia.lib and linked into js.exe (tools/skia_native.py).
  • Firefox 159 builds jsrust with LTO and no longer exports the Rust std, so tools/rust_runtime.py uses the donor's rustc to add a static library containing only std and the allocator.
  • The output is dist/js.exe. A build record (input hashes, commands, tool versions) is written to artifacts/build-runtime.json. Running the build again after changing only src/ is incremental.

6. Assemble a runtime

The runtime also needs the runtime libraries in dist/ and vendor/necko-derived. The simplest way: unzip the runtime package from a release, then overwrite its dist/js.exe with the one you just built.

tools/build.py inputs

Every local path is passed as an argument or an environment variable:

Argument Environment variable Description
--donor-obj RUYIJS_DONOR_OBJ obj directory of the SpiderMonkey standalone build (required)
--firefox-gfx-obj RUYIJS_FIREFOX_GFX_OBJ obj directory of the Firefox 159 browser build, providing the gfx/skia objects for Canvas (required)
--toolchain-bin LLVM bin directory (clang-cl, lld-link)
--vs-root VS directory containing VC and the Windows Kits
--runtime-dll-dir the matching MSVC CRT redistributable
--firefox-font-prefs RUYIJS_FIREFOX_FONT_PREFS optional, see below
--firefox-bindings RUYIJS_FIREFOX_BINDINGS optional, see below

Other options: --prepare-only only prepares the inputs; --no-incremental forces a full rebuild; --jobs N sets the number of compiler processes.

--firefox-font-prefs (dist/bin/greprefs.js) and --firefox-bindings (dom/bindings) come from a full Firefox browser build. Without them, the headers already generated from Firefox 159 in tools/generated/ (firefox_font_prefs.h, firefox_binding_order.h) are used.

macOS arm64

This is the maintainer path for the macOS shell; docs/macos.md has the full steps, the verified network paths and the comparison with Windows. In short:

  • Layout. SpiderMonkey and Necko/NSS come from one official Firefox tree that lives outside this repository and is never committed. A maintainer checkout is two sibling directories:

    firefox/                 official Firefox source, not part of ruyijs
      obj-js-standalone/     SpiderMonkey standalone
      obj-necko/             browser build that produces xpcshell
    ff-ruyijs/               this repository
    
  • Donor builds. Use the tag FIREFOX_159_0_RELEASE and keep both objdirs. mozconfig-js is the Windows one without the --target line. mozconfig-necko is a browser build used for xpcshell:

    mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/obj-necko
    ac_add_options --enable-application=browser
    ac_add_options --enable-optimize
    ac_add_options --disable-debug
    ac_add_options --disable-tests
    ac_add_options --disable-nodejs
    ac_add_options --enable-rust-simd
    ac_add_options --without-wasm-sandboxed-libraries
    

    --disable-nodejs leaves the newtab bundle unfinished, which the helper does not need: stop once obj-necko/dist/bin/xpcshell runs.

  • Helper venv and shell. The Necko helper venv uses Python 3.12. From the repository root, with MACOSX_DEPLOYMENT_TARGET=26.0:

    python3.12 -B tools/bootstrap_macos_helper.py
    python3.12 -B tools/build.py --donor-obj ../firefox-159/obj-js-standalone

    bootstrap_macos_helper.py creates vendor/necko-derived/python (gitignored) with requests, brotli, zstandard, h2, cryptography, backports.zstd and publicsuffixlist; missing wheels come only from the gitignored vendor/necko-derived/wheels via pip --no-index, and an existing venv is kept. Pass --donor-obj (or RUYIJS_DONOR_OBJ) for that build only — do not export it from a long-lived shell profile.

  • Result. The shell is dist/js (with dist/libmozglue.dylib); the C client library is libruyijs_client.dylib. To run the examples, set RUYIJS_SHELL to dist/js and put python/ on PYTHONPATH. The default xpcshell path is firefox-159/obj-necko/dist/bin/xpcshell next to the repository; rename the Firefox directory to match or set RUYIJS_XPCSHELL.

  • Direct-TLS and HTTP/3 libraries. tools/build.py --necko-core-only --firefox-gfx-obj <obj-necko> builds the arm64 libnecko_core.dylib against the donor NSS 3.131 (docs/tls-profile.md); tools/build_necko_quic.py builds libnecko_quic.dylib (docs/closed-route.zh-CN.md).

  • Local wheel. setup.py can build a macosx_26_0_arm64 wheel that places the shell at _runtime/dist/js and copies the donor xpcshell closure to _runtime/dist/necko (plus libnecko_quic.dylib when present). Regenerate the runtime archive with python3.12 -B tools/macos_runtime.py <zip-path>, then store the printed size and sha256, with the release URL, in the macos object of runtime.json. Neither the wheel nor the archive goes to PyPI.

  • Do not commit the Firefox tree, either objdir, dist/js, the helper venv, the macOS runtime zip, libnecko_quic.dylib, neqo-server, native/necko_quic/Cargo.lock, cookies, tokens, proxy credentials or response bodies.

  • Reporting a macOS issue: include the ruyijs commit, the macOS version, the Xcode version, the Python version, the Firefox tag and which of the two objdirs the build used.

After changing C++

From the CONTRIBUTING.md checklist: rebuild the shell (js.exe on Windows, js on macOS) together with the client library (python -B tools/build.py ...), and check the behaviour through both Python and the C ABI. See Contributing and License.

Build tool files

File Responsibility
build.py Build orchestration, input identity and artifact version checks
bootstrap_macos_helper.py Creates the Necko helper venv on macOS; leaves an existing one untouched
donor.py Reads the donor configuration, object lists and library paths
generate.py Generates shell units and embedded resources, keeping the original license headers
native.py Calls the compiler and linker, deploys runtime libraries
client.py Builds the C ABI DLL, import library and public header
geometry_fonts.py Font configuration and HarfBuzz compile inputs
binding_order.py Firefox WebIDL prototype member order table
sanitizer_data.py HTML Sanitizer default configuration data
product_sources.py Production source list, environment baseline profile and domain compile boundaries
compression_native.py / image_native.py / urlpattern_native.py Third-party native library builds
rust_runtime.py Supplies std and the allocator now that Firefox 159's jsrust no longer exports the Rust std
skia_native.py Archives the Skia objects of the Firefox browser build; compile flags for the Canvas bridge units
provenance.py SHA-256 and input identity re-verification
wheel_runtime.py Downloads and verifies the runtime pinned in runtime.json when setup.py builds a wheel
build_necko_quic.py Darwin build of libnecko_quic.dylib (docs/macos.md)
macos_runtime.py Regenerates the macOS runtime archive (docs/macos.md)
generated/ Two headers generated from a Firefox 159 browser build

Clone this wiki locally