Skip to content

Commit

Permalink
Check consistency between prepare and build step (#7986)
Browse files Browse the repository at this point in the history
  • Loading branch information
konstin authored Oct 8, 2024
1 parent 9ae6c4f commit b5dbc32
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 5 deletions.
62 changes: 59 additions & 3 deletions crates/uv-build-backend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ pub enum Error {
Csv(#[from] csv::Error),
#[error("Expected a Python module with an `__init__.py` at: `{}`", _0.user_display())]
MissingModule(PathBuf),
#[error("Inconsistent metadata between prepare and build step: `{0}`")]
InconsistentSteps(&'static str),
}

/// Allow dispatching between writing to a directory, writing to zip and writing to a `.tar.gz`.
Expand Down Expand Up @@ -274,11 +276,17 @@ fn write_hashed(
}

/// Build a wheel from the source tree and place it in the output directory.
pub fn build(source_tree: &Path, wheel_dir: &Path) -> Result<WheelFilename, Error> {
pub fn build(
source_tree: &Path,
wheel_dir: &Path,
metadata_directory: Option<&Path>,
) -> Result<WheelFilename, Error> {
let contents = fs_err::read_to_string(source_tree.join("pyproject.toml"))?;
let pyproject_toml = PyProjectToml::parse(&contents)?;
pyproject_toml.check_build_system();

check_metadata_directory(source_tree, metadata_directory, &pyproject_toml)?;

let filename = WheelFilename {
name: pyproject_toml.name().clone(),
version: pyproject_toml.version().clone(),
Expand Down Expand Up @@ -355,6 +363,54 @@ pub fn metadata(source_tree: &Path, metadata_directory: &Path) -> Result<String,
Ok(dist_info_dir)
}

/// PEP 517 requires that the metadata directory from the prepare metadata call is identical to the
/// build wheel call. This method performs a prudence check that `METADATA` and `entry_points.txt`
/// match.
fn check_metadata_directory(
source_tree: &Path,
metadata_directory: Option<&Path>,
pyproject_toml: &PyProjectToml,
) -> Result<(), Error> {
let Some(metadata_directory) = metadata_directory else {
return Ok(());
};

let dist_info_dir = format!(
"{}-{}.dist-info",
pyproject_toml.name().as_dist_info_name(),
pyproject_toml.version()
);

// `METADATA` is a mandatory file.
let current = pyproject_toml
.to_metadata(source_tree)?
.core_metadata_format();
let previous =
fs_err::read_to_string(metadata_directory.join(&dist_info_dir).join("METADATA"))?;
if previous != current {
return Err(Error::InconsistentSteps("METADATA"));
}

// `entry_points.txt` is not written if it would be empty.
let entrypoints_path = metadata_directory
.join(&dist_info_dir)
.join("entry_points.txt");
match pyproject_toml.to_entry_points()? {
None => {
if entrypoints_path.is_file() {
return Err(Error::InconsistentSteps("entry_points.txt"));
}
}
Some(entrypoints) => {
if fs_err::read_to_string(&entrypoints_path)? != entrypoints {
return Err(Error::InconsistentSteps("entry_points.txt"));
}
}
}

Ok(())
}

/// Add `METADATA` and `entry_points.txt` to the dist-info directory.
///
/// Returns the name of the dist-info directory.
Expand Down Expand Up @@ -495,7 +551,7 @@ mod tests {
fn test_determinism() {
let temp1 = TempDir::new().unwrap();
let uv_backend = Path::new("../../scripts/packages/uv_backend");
build(uv_backend, temp1.path()).unwrap();
build(uv_backend, temp1.path(), None).unwrap();

// Touch the file to check that we don't serialize the last modified date.
fs_err::write(
Expand All @@ -505,7 +561,7 @@ mod tests {
.unwrap();

let temp2 = TempDir::new().unwrap();
build(uv_backend, temp2.path()).unwrap();
build(uv_backend, temp2.path(), None).unwrap();

let wheel_filename = "uv_backend-0.1.0-py3-none-any.whl";
assert_eq!(
Expand Down
5 changes: 3 additions & 2 deletions crates/uv/src/commands/build_backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ pub(crate) fn build_sdist(_sdist_directory: &Path) -> Result<ExitStatus> {
}
pub(crate) fn build_wheel(
wheel_directory: &Path,
_metadata_directory: Option<&Path>,
metadata_directory: Option<&Path>,
) -> Result<ExitStatus> {
let filename = uv_build_backend::build(&env::current_dir()?, wheel_directory)?;
let filename =
uv_build_backend::build(&env::current_dir()?, wheel_directory, metadata_directory)?;
println!("{filename}");
Ok(ExitStatus::Success)
}
Expand Down

0 comments on commit b5dbc32

Please sign in to comment.