Cargo's New Build Directory Layout: A Call for Community Testing and Feedback
By ✦ min read
<p>The Rust Cargo team has introduced a new, nightly-only feature: <code>-Zbuild-dir-new-layout</code>. This flag triggers a significant reorganisation of how intermediate build artifacts are stored. While the build directory structure is considered an internal implementation detail, many tools and workflows inadvertently depend on its current layout. To ensure a smooth transition, the team is asking the community to test the new layout and report any issues.</p>
<h2 id="why-testing-matters">Why This Matters</h2>
<p>Although the layout of the build directory is officially internal, missing features in Cargo have led many projects to rely on the exact arrangement of files. The team has performed a <em>crater run</em> to identify breakages, but that can't cover every custom tool or integration. Community participation is essential to uncover hidden dependencies, report issues to affected projects, and help them update to the new layout or support both old and new simultaneously.</p><figure style="margin:20px 0"><img src="https://www.rust-lang.org/static/images/rust-social-wide.jpg" alt="Cargo's New Build Directory Layout: A Call for Community Testing and Feedback" style="width:100%;height:auto;border-radius:8px" loading="lazy"><figcaption style="font-size:12px;color:#666;margin-top:5px">Source: blog.rust-lang.org</figcaption></figure>
<h2 id="how-to-test">How to Test</h2>
<p>You'll need a nightly compiler from <strong>2026-03-10</strong> or later. Run your test suite, release process, and any other operation that touches <code>build-dir</code> or <code>target-dir</code> with the <code>-Zbuild-dir-new-layout</code> flag. For example:</p>
<pre><code>$ cargo test -Zbuild-dir-new-layout</code></pre>
<p>Note that if you encounter failures, they may not be solely caused by the new layout. Since Cargo 1.91, you can separate intermediate build artifacts (set via <code>CARGO_BUILD_BUILD_DIR=build</code>) from final artifacts (still in <code>target-dir</code>). Verify that your workflow works with only this environment variable set before attributing issues to the layout change.</p>
<p>The team is evaluating whether to make the new layout the default in <a href="https://github.com/rust-lang/cargo/issues/16147">tracking issue #16147</a>.</p>
<h2 id="expected-outcomes">Expected Outcomes</h2>
<ul>
<li><strong>Fix local problems</strong> – Identify and resolve issues in your own projects.</li>
<li><strong>Report problems in upstream tools</strong> – File bugs with a reference to the tracking issue so others can benefit.</li>
<li><strong>Provide feedback</strong> – Share your experience on the tracking issue to inform the final design.</li>
</ul>
<h2 id="known-failure-modes">Known Failure Modes and Solutions</h2>
<h3 id="inferring-bin-paths">Inferring a binary's path from a test's path</h3>
<p>Some tests construct the path to a binary by manipulating the path of the test executable. This will break under the new layout.</p>
<ul>
<li><strong>Solution (Cargo 1.94+):</strong> Use <code>std::env::var_os("CARGO_BIN_EXE_*")</code>.</li>
<li><strong>Fallback:</strong> Use <code>env!("CARGO_BIN_EXE_*")</code> with the <em>CARGO_BIN_EXE_</em> environment variable.</li>
</ul>
<h3 id="build-scripts-looking-up-target-dir">Build scripts looking up <code>target-dir</code> from their binary or <code>OUT_DIR</code></h3>
<p>Build scripts sometimes derive the location of the target directory based on their own binary path or <code>OUT_DIR</code>. This assumption will no longer hold. See <a href="https://github.com/rust-lang/cargo/issues/13663">issue #13663</a>.</p>
<ul>
<li><strong>Solution:</strong> Update current workarounds to support the new layout. Use the <code>CARGO_TARGET_DIR</code> environment variable instead of inferring.</li>
</ul>
<h3 id="looking-up-user-requested-artifacts">Looking up user‑requested artifacts from <code>rustc</code></h3>
<p>Tools that parse <code>rustc</code> output to locate built artifacts (e.g., binaries, libraries) may rely on the old directory structure. See <a href="https://github.com/rust-lang/cargo/issues/13672">issue #13672</a>.</p>
<ul>
<li><strong>Solution:</strong> Update tools to use Cargo's <code>--message-format=json</code> to reliably find artifacts, rather than scanning the build directory.</li>
</ul>
<h2 id="library-compatibility-status">Library Compatibility Status</h2>
<p>The following popular libraries have been checked. <em>Status as of publication.</em></p>
<ul>
<li><strong>assert_cmd</strong> – fixed</li>
<li><strong>cli_test_dir</strong> – <a href="https://github.com/assert-rs/cli_test_dir/issues/65">issue #65</a></li>
<li><strong>compiletest_rs</strong> – <a href="https://github.com/rust-lang/compiletest-rs/issues/309">issue #309</a></li>
<li><strong>executable-path</strong> – fixed</li>
<li><strong>snapbox</strong> – fixed</li>
<li><strong>term-transcript</strong> – <a href="https://github.com/assert-rs/term-transcript/issues/269">issue #269</a></li>
<li><strong>test_bin</strong> – <a href="https://github.com/SarthakMakhija/test_bin/issues/13">issue #13</a></li>
<li><strong>trycmd</strong> – fixed</li>
</ul>
<h2 id="what-changes">What Changes and What Stays the Same</h2>
<h3 id="unchanged">What Is <em>Not</em> Changing</h3>
<ul>
<li>The layout of <strong>final artifacts</strong> inside <code>target-dir</code> remains unchanged.</li>
<li>The nesting of build artifacts under the profile and, if specified, the target tuple (e.g., <code>debug/</code> or <code>release/</code>) is preserved.</li>
</ul>
<h3 id="changed">What Is Changing</h3>
<p>The new layout switches from grouping artifacts by <strong>content type</strong> (e.g., <code>build/</code>, <code>deps/</code>, <code>.fingerprint/</code>) to scoping them by <strong>package name</strong> plus a hash of the build unit and its inputs. This makes the directory structure more predictable and scalable.</p>
<p>For example, the current layout (approximate) for a workspace with packages named <code>lib</code> and <code>bin</code>, each having a build script:</p>
<pre><code>build-dir/
├── CACHEDIR.TAG
└── debug/
├── .cargo-lock
├── .fingerprint/
│ ├── bin-[BUILD_SCRIPT_RUN_HASH]/
│ ├── bin-[BUILD_SCRIPT_BIN_HASH]/
│ ├── bin-[HASH]/
│ ├── lib-[BUILD_SCRIPT_RUN_HASH]/
│ ├── lib-[BUILD_SCRIPT_BIN_HASH]/
│ └── lib-[HASH]/
├── build/
│ ├── bin-[BIN_HASH]/
│ ├── bin-[RUN_HASH]/
│ ...
</code></pre>
<p>Under the new layout, all artifacts for a given package will be grouped under a directory named after the package, with hashing applied per unit. This eliminates cross‑package contamination and makes caching more robust.</p>
<h2 id="conclusion">Conclusion</h2>
<p>The new build directory layout is a major internal improvement for Cargo, but it requires community validation before becoming the default. Please take a few minutes to test your projects with <code>-Zbuild-dir-new-layout</code> and report any problems. Your feedback will help make the transition smooth for everyone. For more details and to contribute to the discussion, visit <a href="https://github.com/rust-lang/cargo/issues/16147">tracking issue #16147</a>.</p>
Tags: