Skip to content

Make the Rust toolchain a documented prerequisite, and stop uv racing rustup - #172

Merged
ericeil merged 3 commits into
masterfrom
eric/buildfix
Aug 18, 2026
Merged

Make the Rust toolchain a documented prerequisite, and stop uv racing rustup#172
ericeil merged 3 commits into
masterfrom
eric/buildfix

Conversation

@ericeil

@ericeil ericeil commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

The failure

× Failed to build `run-confined @ .../rust/run-confined`
  ╰─▶ Call to `maturin.build_wheel` failed (exit status: 1)
      info: syncing channel updates for '1.96-x86_64-unknown-linux-gnu'
      error: component download failed for cargo-x86_64-unknown-linux-gnu: could not rename
      downloaded file from '.../downloads/ecc53a3c….partial' to '.../downloads/ecc53a3c…':
      No such file or directory (os error 2)

Nothing in that message names the actual cause. dev is one of uv's default groups and pulls in apps, so a plain uv sync — or any uv run, which revalidates path dependencies — builds the maturin crates under rust/. On a machine that doesn't yet have the toolchain rust-toolchain.toml pins, each crate's cargo call asks rustup to install it, and rustup's install path is not safe against concurrent invocations: whichever call wins clears $RUSTUP_HOME/downloads and the others die renaming a half-downloaded component.

CI already installs the toolchain in a step of its own for exactly this reason (.github/workflows/pytest.yml). Nothing carried that over to dev machines.

What's here

README.mdrustup joins the Prerequisites list, with a section giving the argless rustup toolchain install --no-self-update (it takes channel, profile and components from rust-toolchain.toml, so the pin stays in one place), the note that a distro cargo package is not enough, and the --no-dev escape hatch for anyone not working on the Rust side.

CLAUDE.md — the same, on the pre-validation pass, since the sync recipe there is the command that trips this.

pyproject.tomlconcurrent-builds = 1 under [tool.uv], closing the uv-driven half of the race for whoever skips the docs.

The docs matter more than the setting: fixing machine state covers every concurrent cargo caller, whereas concurrent-builds only covers builds uv itself drives. rust-analyzer running cargo metadata on the workspace the moment the repo opens in an editor races identically, and no uv setting reaches it.

Measured, not assumed

Max crates building at once, on master's two maturin crates:

configuration concurrent builds
no key, no env (uv default) 2
[tool.uv] concurrent-builds = 1 1
UV_CONCURRENT_BUILDS=1 1
UV_CONCURRENT_BUILDS=8 2

So the config key genuinely applies rather than merely parsing, and the env var overrides it. Serializing costs next to nothing here: the crates share one cargo workspace, so their builds already contend on rust/target's build-dir lock.

Verification

  • pytest -m "not expensive"956 passed, 12 deselected
  • pyright0 errors

Note for reviewers

This bites whenever the pinned toolchain is missing, not only on a fresh checkout — so the next bump to rust-toolchain.toml's channel re-arms it for the whole team at once. That's the case worth keeping in mind if this text gets edited down further.

🤖 Generated with Claude Code

ericeil and others added 2 commits August 18, 2026 11:14
A fresh checkout of master fails its first sync with an error that names neither
rustup's on-demand install nor the concurrency that broke it:

    error: component download failed for cargo-x86_64-unknown-linux-gnu:
    could not rename downloaded file ... No such file or directory (os error 2)

`dev` is one of uv's default groups and pulls in `apps`, so a plain `uv sync` --
or any `uv run`, which revalidates path deps -- builds the maturin crates under
rust/. On a machine that does not yet have the toolchain rust-toolchain.toml
pins, each crate's cargo call asks rustup to install it, and rustup's install
path is not safe against concurrent invocations: whichever call wins clears
$RUSTUP_HOME/downloads and the others die renaming a half-downloaded component.
CI already installs the toolchain in a step of its own for this exact reason
(.github/workflows/pytest.yml); nothing carried that over to dev machines.

Two parts:

* README and CLAUDE.md name rustup as a prerequisite and give the argless
  `rustup toolchain install`, which takes channel, profile and components from
  rust-toolchain.toml so the pin stays in one place. Fixing machine state is
  what covers every concurrent cargo caller rather than uv's builds alone --
  rust-analyzer's `cargo metadata` on the workspace races identically, and no
  uv setting reaches it.
* concurrent-builds = 1 closes the uv-driven half for whoever skips the docs.

Measured on master (two maturin crates), max crates building at once: 2 with no
setting, 1 with the key, 1 with UV_CONCURRENT_BUILDS=1, and 2 again under
UV_CONCURRENT_BUILDS=8 -- the env var overrides the key, and the key is not
merely parsed. Serializing costs next to nothing here: the crates share one
cargo workspace, so their builds already contend on rust/target's build-dir
lock.

pytest -m "not expensive": 956 passed. pyright: 0 errors.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A missing toolchain is what arms the race, so it is not a fresh-checkout-only
problem: the next bump to rust-toolchain.toml's `channel` re-arms it for
everyone at once, and the install step reads as one-time without this.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ericeil
ericeil requested a review from jtoman August 18, 2026 18:55
@jtoman

jtoman commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

for whoever skips the docs.

I don't think I skipped any docs okay!?!

@ericeil
ericeil merged commit 4af1ad0 into master Aug 18, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants