Building
A recent stable Rust toolchain (the workspace is edition 2024 — Rust 1.85+) and, for the
documentation gates and the two npm projects, Node. CI runs Node 24, the active LTS, whose npm
11 is the one allowScripts below assumes. From the repo root:
cargo build # the everyday buildcargo run -p standalone --release # the appcargo nextest run --workspace # the whole suitecargo clippy --workspace --all-targets -- -D warningscargo fmt --all --check--workspace is load-bearing on the test and lint rows rather than a flourish: the C ABI crate sits
outside the workspace’s default members, so a bare cargo nextest run skips the ABI conformance
suite and a bare cargo clippy stops linting the C ABI, and both come back green while covering
nothing.
The headless capture CLI and the visual-QA harness have their own page, Headless capture and video.
The exe’s size is read by the build that makes it
NFR §4 caps the release exe softly, and the figure is a property
of a build rather than of the tree: it moves with the toolchain, the profile and the feature set.
So nothing in the everyday loop or in ci.yml measures it. The two standalone packaging recipes do —
packaging/windows/stage.ps1 and packaging/macos/bundle.sh — on every run, printing the length
beside the build that produced it and warning past 90 % of the cap, never failing
(ADR-0231).
To read it on a box that runs neither, build the release binary and read its length; the
composition behind the cap, and how it was taken, is written down in NFR §4 so the next reading can
be compared with the last.
A fresh Arch Linux checkout
Everything below uses Arch package names. libpulse, pkgconf, wayland, libxkbcommon,
ffmpeg, nodejs and python are usually present already on a desktop install:
sudo pacman -S --needed rustup vulkan-swrast vulkan-tools \ cargo-nextest cargo-release cargo-deny uv \ libpulse pkgconf wayland libxkbcommon ffmpeg nodejs npm pythonrustup show # inside the checkout: installs the toolchain rust-toolchain.toml pinsgit config core.hooksPath .githooks # the pre-push gate, opt-in per clonenpm --prefix studio ci # the studio's dependencies, which the hook's studio step needscargo buildvulkan-swrast is lavapipe, the software Vulkan rasterizer the adapter-agnostic tests run on
(ADR-0242).
vulkaninfo --summary should list it as llvmpipe beside the hardware GPUs. uv provides the older
CPython that the diffusion sidecar’s pinned torch needs. Desktop audio reaches the standalone through
PulseAudio’s monitor source, which PipeWire serves via pipewire-pulse
(ADR-0131).
The whole gate is green on Arch. Measured on 2026-09-22 on an Arch laptop (RTX 3080 Laptop
dGPU, AMD Vega iGPU, lavapipe from Mesa 26.2.2), with target/ already built:
| Run | Wall time | Result |
|---|---|---|
.githooks/pre-push, run by hand so every step runs | 333 s | green, no step skipped; the test step alone took 297 s, 1695 passed and 86 skipped |
cargo nextest run --workspace | 454 s | 1774 passed, 7 skipped |
That table is the first green, taken while the golden baselines were still WARP’s. Two things about a run on this box today:
- The pinned-baseline tests assert on Linux’s lavapipe, and only there. The golden baselines
are lavapipe captures, blessed on this box
(ADR-0242).
common::baseline_adapterasserts a comparison only on Linux’s lavapipe. On any other adapter, DX12 WARP included, it prints each fixture’s reading and skips. So a golden mismatch fails on the Arch box and on CI’subuntu-latestarm, and is only reported on Windows. The runner’s Mesa is a different build from this box’s, so a red there is first a cross-Mesa reading to take. A rebless (RLX_BLESS) is refused off lavapipe, and is done on this box only. - The hardware tests take the high-performance adapter, which on this laptop is the RTX 3080
dGPU and not the iGPU
(ADR-0243).
Each hardware build prints a
hardware adapter:line naming the GPU and its driver, so a reading it produces names its machine.
npm 11 runs a dependency’s install script only when allowScripts names it. The studio’s
package.json lists each approved package pinned as pkg@version, and a new entry is a reviewed
edit, made with npm --prefix studio install-scripts approve <pkg> and never with --all
(ADR-0244).
A bump strands its pinned entry, and npm ci then succeeds while silently skipping the script, so
a bump of a listed package re-approves it. npm --prefix studio install-scripts ls should print
No packages with unreviewed install scripts. site/ has no field: the one script its graph holds,
esbuild’s, is not needed for its build. Electron 44 has no install script at all; it downloads
its binary the first time it runs, which npx --prefix studio electron --version triggers.
The studio run from source reads "playerPath" from ~/.config/ritmolux-studio/settings.json.
That is settings.json in Electron’s per-user directory, which on Linux is
$XDG_CONFIG_HOME/ritmolux-studio/, falling back to ~/.config/ when XDG_CONFIG_HOME is unset.
Set "playerPath" there to point the studio at your own target/release/ritmolux. The directory is
named after the "name" in studio/package.json, so it is lowercase, unlike the player’s own
~/.local/share/Ritmolux/.
No linker override on Linux. The toolchain’s default rust-lld relinks the 52 MB suite test
binary in about 200 ms. mold 2.42 saves about 15 ms of that, and makes no measurable difference to
a cold rebuild of rlx-core’s tests, so no machine-local config file is suggested.
Machine setup: the linker override (opt-in, and inert if skipped)
Every worktree compiles into its own target/. The one machine-local override is the MSVC
linker, and it lives in a file one directory above the worktrees — WORK/.cargo/config.toml,
beside ritmolux/ rather than inside it — so cargo’s ancestor walk finds it from
whichever lane is building and a new lane needs no setup of its own:
[target.x86_64-pc-windows-msvc]linker = "rust-lld.exe"That is the whole file. rust-lld.exe is not on PATH and still resolves — rustc finds it in its
own sysroot, so no explicit path and no linker-flavor flag are needed. It took the cold path to
every test binary from 171 s to 145 s while moving no golden (ADR-0141’s Outcome, which stands).
The override is Windows-only. A Linux checkout has no WORK/.cargo/config.toml and currently
needs none: since Rust 1.90, x86_64-unknown-linux-gnu already links with the bundled rust-lld
by default, and the paragraph above, No linker override on Linux, has what mold was measured
against it.
It is never committed, and it cannot be. The macOS arm has a different linker story, and
reaching rust-lld any other way means naming a sysroot path specific to one machine. Like
git config core.hooksPath .githooks
(ADR-0033), this is
opt-in per machine and inert when skipped — a machine without the file builds correctly, just
with the default linker.
There is no shared artifact store, and a [build] target-dir redirect must not go back in this
file. ADR-0141 pointed every worktree at
one store; ADR-0147
revoked that half, because the worktree path is not in cargo’s fingerprint — two lanes with the
same layout and dependency graph are indistinguishable, so one lane is served the other’s compiled
rlx-core as fresh. Plan 0115’s lane hit no method named open_tap found for struct Renderer
against committed source that defines it. The failure is silent, it is not a cache miss, and no gate
catches it. The linker half above is not implicated and stays.
The cost that comes back with the revocation is disk, and it is not gated:
- ADR-0053’s “disk cost is severe and
recurring” is live again. One lane held ~8 GB in
target/debug/incrementaland filled the disk mid-session. Remove a finished lane’s worktree — on Windowsgit worktree removefails withPermission deniedwhile any shell still has its working directory inside it. The Disk section below is what to do short of removing one.
Dependencies compile with no debug info (committed, unlike the override above)
[profile.dev.package."*"] in the root Cargo.toml carries debug = 0, so every dependency —
wgpu, naga, winit, windows-rs — compiles with no debug info at all, while every workspace crate
keeps profile.dev’s line-tables-only. Unlike the linker override this is committed and applies
to every clone: it names nothing machine-specific, and a build profile that silently differs
between two checkouts is the hazard ADR-0147 exists to end.
What it costs you: a backtrace frame inside one of those crates carries no line number. A wgpu
validation failure is normally diagnosed from its message rather than from a backtrace line, which
is what makes that affordable — and on the day it is not, delete the debug = 0 line and
rebuild. That buys the line numbers back at the price of a full rebuild of the dependency graph at
opt-level = 2 — 87 s on the reference machine, and the same again when the line goes back. Do not
commit the deletion.
Why the line is there: MSVC emits a separate .pdb per linked target and packs no split debuginfo,
so the dependency graph’s line tables are duplicated into every one of the workspace’s test
binaries — 25.5 MB per binary, measured, which the setting stops emitting. ADR-0165.
Built from f4f807a at version 0.178.0. This site tracks main and is not versioned per release.