Skip to this page's content

Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

The shared target directory and build lock

mise run tasks go through scripts/build-lock.sh, which exports CARGO_TARGET_DIR to target-shared/ beside the main checkout, so every worktree’s mise run build compiles into that one directory. scripts/build-lock.sh --print-target-dir resolves the path.

A bare cargo does not share it

.cargo/config.toml sets target-dir = "target-shared", a path relative to the config file’s own directory. That file is checked in, so every worktree has its own copy, and cargo’s closest-config-wins resolution picks it. Without the export above, cargo build in a worktree compiles into a target-shared/ beside the worktree. Confirm which one applies with cargo metadata --format-version 1 --no-deps | jq -r .target_directory. The sharing is a property of the mise run wrapper, not of the config, which is why mise run sweep sweeps each worktree’s directory too.

Why one directory

Cargo takes an exclusive lock on the target directory, so a second build prints Blocking waiting for file lock on build directory and waits instead of running alongside the first. jobs = 4 caps a single cargo process but does not compose across worktrees: four agents building at once meant sixteen jobs, which oversubscribed RAM, spilled to a swap file on the same NVMe as the target dirs, and left the machine stalled on I/O rather than short of CPU. Pointing CARGO_TARGET_DIR somewhere private to skip the wait restores that pile-up.

The build lock

scripts/build-lock.sh also takes a lock in the shared git dir and names the holder while you wait. MICOLD_NO_BUILD_LOCK=1 skips it for a one-off run; cargo’s own lock still applies. The interactive tasks (run, showcase, daemon) skip it by design, since they stay in the foreground for as long as the app is open.

Target dirs grow without bound; mise run sweep reclaims them.