Skip to content

fix(sdist): regenerate Cargo.lock when workspace members are removed#3192

Open
ckhordiasma wants to merge 2 commits into
PyO3:mainfrom
ckhordiasma:fix/sdist-cargolock
Open

fix(sdist): regenerate Cargo.lock when workspace members are removed#3192
ckhordiasma wants to merge 2 commits into
PyO3:mainfrom
ckhordiasma:fix/sdist-cargolock

Conversation

@ckhordiasma
Copy link
Copy Markdown

@ckhordiasma ckhordiasma commented May 19, 2026

Summary

Fixes #2609.

When maturin builds an sdist from a workspace, rewrite_cargo_toml strips workspace members
not needed by the package. The Cargo.lock was copied verbatim and still referenced those
removed crates, so cargo build --locked inside the sdist would fail.

After all sdist entries are assembled in the virtual writer, materialize them to a temp
directory, run cargo generate-lockfile to reconcile the lockfile, and replace the tracker
entry with the regenerated Cargo.lock. This only runs for workspace crates that have a
Cargo.lock entry in the sdist.

Changes

  • Add materialize_to and replace_bytes helpers on VirtualWriter<SDistWriter>
  • Add regenerate_cargo_lock step after add_workspace_manifest in the sdist builder
  • Add regression test that builds an sdist from a two-member workspace (where the sibling has
    a unique dependency), unpacks it, and asserts cargo metadata --frozen succeeds

Test plan

  • cargo test --test run -- sdist_workspace_removed_members_cargo_lock passes with the fix
  • Same test fails on main without the fix (verified on a separate branch)
  • cargo fmt and cargo clippy clean

🤖 Generated with Claude Code — ~40 back-and-forth messages covering issue analysis, implementation, line-by-line code walkthrough, test authoring, failure verification, style review, and PR creation.

ckhordiasma and others added 2 commits May 19, 2026 09:43
Fixes PyO3#2609.

When maturin builds an sdist from a workspace, `rewrite_cargo_toml`
strips workspace members not needed by the package. The Cargo.lock was
copied verbatim and still referenced those removed crates, so
`cargo build --locked` inside the sdist would fail.

After all sdist entries are assembled, materialize them to a temp
directory, run `cargo generate-lockfile` to reconcile the lockfile,
and replace the tracker entry with the regenerated Cargo.lock.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Exercises the fix for PyO3#2609: builds an sdist from a workspace member
while a sibling member (`devtool`, with a unique `rand` dependency)
is stripped, then asserts `cargo metadata --frozen` succeeds in the
unpacked sdist.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes sdists produced from Cargo workspaces where rewrite_cargo_toml removes unneeded workspace members but the copied Cargo.lock still references them, causing cargo build --locked / cargo metadata --frozen to fail inside the unpacked sdist.

Changes:

  • Add VirtualWriter<SDistWriter>::materialize_to and replace_bytes helpers to support post-processing tracked sdist entries.
  • After assembling workspace-related sdist entries, materialize the sdist to a temp dir, run cargo generate-lockfile, and replace the sdist’s Cargo.lock with the regenerated version.
  • Add a regression test that constructs a two-member workspace, builds an sdist for one member, unpacks it, and asserts cargo metadata --frozen succeeds.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
tests/run/sdist.rs Adds regression coverage ensuring workspace-member sdists have a usable Cargo.lock.
src/source_distribution/mod.rs Adds regenerate_cargo_lock step in the sdist build pipeline to reconcile lockfiles after workspace-member stripping.
src/module_writer/virtual_writer.rs Adds helpers to materialize tracked sdist entries to disk and replace a tracked file’s bytes in-memory.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +750 to +757
let logical_manifest_dir = ctx
.project
.manifest_path
.parent()
.map(Path::to_path_buf)
.unwrap_or_else(|| ctx.abs_manifest_dir.clone());
let is_in_workspace = ctx.workspace_root.as_std_path() != logical_manifest_dir;
if !is_in_workspace {
ArchiveSource::File(f) => {
fs_err::copy(&f.path, &dest)?;
}
}
Comment on lines +566 to +582
/// Write all tracked entries to a directory on disk.
pub(crate) fn materialize_to(&self, dir: &Path) -> Result<()> {
for (target, source) in &self.tracker {
let dest = dir.join(target);
if let Some(parent) = dest.parent() {
fs_err::create_dir_all(parent)?;
}
match source {
ArchiveSource::Generated(g) => {
fs_err::write(&dest, &g.data)?;
}
ArchiveSource::File(f) => {
fs_err::copy(&f.path, &dest)?;
}
}
}
Ok(())
Comment on lines +586 to +594
pub(crate) fn replace_bytes(&mut self, target: &Path, data: Vec<u8>) {
self.tracker.insert(
target.to_path_buf(),
ArchiveSource::Generated(GeneratedSourceData {
data,
path: None,
executable: false,
}),
);
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.

sdist generated from workspace contains outdated Cargo.lock

2 participants