vike
Developer

Building

The workspace roster, the feature lanes a default build never compiles, the one committed cargo config, and why the xtask convention is spelled differently here.

Vike Trader is a Cargo workspace of small crates. A plain cargo build --workspace is a real build of most of it — but not of all of it, and the gap is the first thing worth understanding before you change anything.

The roster is derived, never written down

CI does not read a hand-maintained list of crates. It computes one: sorted(names - EXCLUDE_FROM_CI) over the workspace members cargo metadata reports. That is xtask/src/ci/mod.rs's ci_crates, and the consequence is the point — a crate joins the merge gate the moment it joins [workspace].members, with nothing to edit to let it in.

xtask/src/ci/tables.rs's EXCLUDE_FROM_CI is the whole of the exception, and each row carries its reason: the vendored IBKR wire-protocol copy (exercised through its own feature lane); vike-desktop, the egui/wgpu desktop binary, whose eframe + wgpu closure matches no other lane's cache and which is instead checked, linted and unit-tested by its own dedicated job; and vike-backfill, which only builds and tests under features. xtask itself is deliberately absent from that list — excluding the tool that derives the merge gate would leave it as the one crate the merge gate never compiles.

A default build is not the whole tree

Large parts of the workspace are behind off-by-default Cargo features, and the reason is usually that a default build should not compile them: an order signer, a proprietary SDK shim, a remote control channel, a DataFusion dependency. Each of those configurations gets a feature lane.

Two files own the lanes between them, and they own different halves. xtask/src/ci/tables.rs's FEATURE_SUITES decides which suite keys the plan may emit and which crates trigger each one; the shell script CI runs decides, in its case arms, what each key actually runs. xtask/src/lib.rs's header states that split, and records that moving the lane commands into the crate would be a separate change with its own gate to rewrite. The features job wires the first straight into the second, so the two key sets are one roster written twice — which is why they are gated against each other. Nearly every row there carries a written argument for the coverage hole it closes, and tables.rs states the rule for editing it: a row there is not a preference, it is a claim about what CI would otherwise fail to test.

A lane is defined in one place, and this page is not it

What a lane compiles is decided by the case arms of the feature-suite shell script CI executes; xtask/src/lib.rs's header records exactly that split. That script ships with the private build tooling rather than the published tree, so no summary of it — this page included — can be checked against it. What you can open is the other half: xtask/src/ci/tables.rs's FEATURE_SUITES names every suite key and the crates that trigger it.

Treat a lane's crate list here as history, not as a spelling. Two lanes have already been widened, and each widening is argued on its own trigger row in that table. The FXCM one twice: first when the live mount arm landed behind vike-mount's own fxcm feature, then when the release grew an FXCM-linked daemon asset. The IBKR one twice: first when the live-mount chain above the bridge crate turned out to be compiled by nothing, then again when the daemon itself grew an ibkr feature and the chain reached one crate further. Re-spelling a lane from a prose copy is how a chain silently drops out of it.

The one committed cargo config

Two files configure the compiler for everyone who clones the repository: .cargo/config.toml and the root Cargo.toml's profile tables. Neither is anybody's code, both are read on every build, and a change to either arrives without a call site to review it — which is why crates/vike-ops/tests/build_config_gate.rs exists.

It reads every file cargo would honour, not the one at the root. Cargo merges .cargo/config.toml — and the pre-1.39 extensionless .cargo/config — out of every ancestor directory of wherever it was invoked, closest-wins, so a config placed under crates/ outranks the repository root for the normal inner loop. only_the_repository_root_carries_a_cargo_config refuses that shape outright: a setting that applies depending on which directory you happen to be standing in cannot be reviewed by reading a diff.

Two content rules follow. every_cargo_config_table_is_windows_scoped requires every table to be scoped to x86_64-pc-windows-msvc, as an allowlist rather than a banned-table list, because a banned list is a hand copy of cargo's vocabulary and would rot the day cargo adds a table. And no_committed_cargo_setting_decides_which_program_runs refuses rustc-wrapper, rustc, rustdoc and runner in every table, the Windows one included. linker is deliberately not in that set: a linker receives object files and rustc's own link arguments, while rustc-wrapper and rustc receive every source file in the tree, rustdoc runs the doctests, and runner receives the compiled test binary — which would turn cargo test, the command every gate runs under, into "execute a program of this file's choosing" before any assertion in any of them is reached.

The guarantee, exactly: a committed cargo setting in this repository may name the linker the Windows dev box runs, and no other program, on no other host.

Why just xtask, and not cargo xtask

The usual convention for a repository's build tool is a cargo xtask alias. There is deliberately no such alias here. An alias lives in .cargo/config.toml; build_config_gate.rs requires every leaf path in that file to sit under target.x86_64-pc-windows-msvc; and cargo does not allow [alias] to be target-scoped. No spelling of the convention passes the gate that protects the Linux boxes holding the credential store, so xtask/src/main.rs says so in its own header and a just recipe is the entry point instead: just xtask <cmd>, which is cargo run -p xtask -- <cmd>.

That tool has four commands and one consumer each — and no fifth, because a command nobody consumes is a surface that rots unobserved, which is the defect the crate was written to remove:

commandconsumeroutput
cratesthe local recipes' ci_crates :=one line, -p a -p b …
affectedboth workflows' plan stepseven key=value lines
feature-lanesa human, and the gateskey<TAB>trigger,trigger
docs-binsthe release's bins.json docs-data assetJSON: every workspace [[bin]]

CI derivation explains how affected selects and how the local recipes are held to the same spelling; Testing covers the tiers that run over what you built.

On this page