Skip to content
Русский

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:

Terminal window
cargo build # the everyday build
cargo run -p standalone --release # the app
cargo nextest run --workspace # the whole suite
cargo clippy --workspace --all-targets -- -D warnings
cargo 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:

Terminal window
sudo pacman -S --needed rustup vulkan-swrast vulkan-tools \
cargo-nextest cargo-release cargo-deny uv \
libpulse pkgconf wayland libxkbcommon ffmpeg nodejs npm python
rustup show # inside the checkout: installs the toolchain rust-toolchain.toml pins
git config core.hooksPath .githooks # the pre-push gate, opt-in per clone
npm --prefix studio ci # the studio's dependencies, which the hook's studio step needs
cargo build

vulkan-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:

RunWall timeResult
.githooks/pre-push, run by hand so every step runs333 sgreen, no step skipped; the test step alone took 297 s, 1695 passed and 86 skipped
cargo nextest run --workspace454 s1774 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_adapter asserts 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’s ubuntu-latest arm, 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/incremental and filled the disk mid-session. Remove a finished lane’s worktree — on Windows git worktree remove fails with Permission denied while 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.