Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 23 additions & 12 deletions crates/uv/src/commands/build_frontend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ pub(crate) enum Error {
InvalidBuiltSourceDistFilename(#[source] uv_distribution_filename::SourceDistFilenameError),
#[error("The built wheel has an invalid filename")]
InvalidBuiltWheelFilename(#[source] uv_distribution_filename::WheelFilenameError),
#[error("The source distribution declares name {0}, but the wheel declares name {1}")]
NameMismatch(PackageName, PackageName),
#[error("The source distribution declares version {0}, but the wheel declares version {1}")]
VersionMismatch(Version, Version),
}
Expand Down Expand Up @@ -793,7 +795,7 @@ async fn build_package(
subdirectory,
version_id,
build_output,
Some(sdist_build.normalized_filename().version()),
Some(sdist_build.normalized_filename()),
)
.await?;
build_results.push(wheel_build);
Expand Down Expand Up @@ -865,7 +867,7 @@ async fn build_package(
subdirectory,
version_id,
build_output,
Some(sdist_build.normalized_filename().version()),
Some(sdist_build.normalized_filename()),
)
.await?;
build_results.push(sdist_build);
Expand All @@ -881,13 +883,13 @@ async fn build_package(
uv_extract::stream::archive(source.path().display(), reader, ext, temp_dir.path())
.await?;

// If the source distribution has a version in its filename, check the version.
let version = source
// If the source distribution has a normalized filename, check its identity.
let source_dist = source
.path()
.file_name()
.and_then(|filename| filename.to_str())
.and_then(|filename| SourceDistFilename::parsed_normalized_filename(filename).ok())
.map(|filename| filename.version);
.map(DistFilename::SourceDistFilename);

// Extract the top-level directory from the archive.
let extracted = match uv_extract::strip_component(temp_dir.path()) {
Expand All @@ -909,7 +911,7 @@ async fn build_package(
subdirectory,
version_id,
build_output,
version.as_ref(),
source_dist.as_ref(),
)
.await?;
build_results.push(wheel_build);
Expand Down Expand Up @@ -1068,8 +1070,8 @@ async fn build_wheel(
subdirectory: Option<&Path>,
version_id: Option<&str>,
build_output: BuildOutput,
// Used for checking version consistency
version: Option<&Version>,
// Used for checking source distribution and wheel consistency
source_dist: Option<&DistFilename>,
) -> Result<BuildMessage, Error> {
let build_message = match action {
BuildAction::List => {
Expand Down Expand Up @@ -1155,10 +1157,19 @@ async fn build_wheel(
}
}
};
if let Some(expected) = version {
let actual = build_message.normalized_filename().version();
if expected != actual {
return Err(Error::VersionMismatch(expected.clone(), actual.clone()));
if let Some(expected) = source_dist {
let actual = build_message.normalized_filename();
if expected.name() != actual.name() {
return Err(Error::NameMismatch(
expected.name().clone(),
actual.name().clone(),
));
}
if expected.version() != actual.version() {
return Err(Error::VersionMismatch(
expected.version().clone(),
actual.version().clone(),
));
}
}
Ok(build_message)
Expand Down
56 changes: 48 additions & 8 deletions crates/uv/tests/build/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2666,6 +2666,46 @@ fn build_version_mismatch() -> Result<()> {
Ok(())
}

/// A backend must not return an sdist and wheel for different projects.
#[test]
fn build_name_mismatch() -> Result<()> {
let context = uv_test::test_context!("3.12");
let project = context.temp_dir.child("project");
project.child("pyproject.toml").write_str(indoc! {r#"
[build-system]
requires = []
build-backend = "backend"
backend-path = ["."]
"#})?;
project.child("backend.py").write_str(indoc! {r#"
from pathlib import Path

def build_sdist(sdist_directory, config_settings=None):
filename = "alpha-1.0.0.tar.gz"
Path(sdist_directory, filename).touch()
return filename

def build_wheel(wheel_directory, config_settings=None, metadata_directory=None):
filename = "beta-1.0.0-py3-none-any.whl"
Path(wheel_directory, filename).touch()
return filename
"#})?;

uv_snapshot!(context.filters(), context.build().arg("--sdist").arg("--wheel").current_dir(&project), @"
success: false
exit_code: 2
----- stdout -----

----- stderr -----
Building source distribution...
Building wheel...
error: Failed to build `[TEMP_DIR]/project`
Caused by: The source distribution declares name alpha, but the wheel declares name beta
");

Ok(())
}

#[cfg(unix)] // Symlinks aren't universally available on windows.
#[test]
fn build_with_symlink() -> Result<()> {
Expand Down Expand Up @@ -2763,16 +2803,16 @@ fn build_workspace_virtual_root() -> Result<()> {
"#})?;

uv_snapshot!(context.filters(), context.build().arg("--no-build-logs"), @"
success: true
exit_code: 0
success: false
exit_code: 2
----- stdout -----

----- stderr -----
Building source distribution...
warning: `[TEMP_DIR]/` appears to be a workspace root without a Python project; consider using `uv sync` to install the workspace, or add a `[build-system]` table to `pyproject.toml`
Building wheel from source distribution...
Successfully built dist/cache-0.0.0.tar.gz
Successfully built dist/UNKNOWN-0.0.0-py3-none-any.whl
error: Failed to build `[TEMP_DIR]/`
Caused by: The source distribution declares name cache, but the wheel declares name unknown
");
Ok(())
}
Expand All @@ -2792,16 +2832,16 @@ fn build_pyproject_toml_not_a_project() -> Result<()> {
"})?;

uv_snapshot!(context.filters(), context.build().arg("--no-build-logs"), @"
success: true
exit_code: 0
success: false
exit_code: 2
----- stdout -----

----- stderr -----
Building source distribution...
warning: `[TEMP_DIR]/` does not appear to be a Python project, as the `pyproject.toml` does not include a `[build-system]` table, and neither `setup.py` nor `setup.cfg` are present in the directory
Building wheel from source distribution...
Successfully built dist/cache-0.0.0.tar.gz
Successfully built dist/UNKNOWN-0.0.0-py3-none-any.whl
error: Failed to build `[TEMP_DIR]/`
Caused by: The source distribution declares name cache, but the wheel declares name unknown
");
Ok(())
}
Expand Down
Loading