Skip to content

Commit 02a7bb4

Browse files
authored
Minimal wheel settings (#9085)
A small refactoring and minimal wheel settings. source tree -> source dist -> wheel is working, which we'll add tests for in the next PR.
1 parent 4ac78f6 commit 02a7bb4

File tree

3 files changed

+97
-44
lines changed

3 files changed

+97
-44
lines changed

crates/uv-build-backend/src/lib.rs

+79-41
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ use std::fs::FileType;
1111
use std::io::{BufReader, Cursor, Read, Write};
1212
use std::path::{Path, PathBuf, StripPrefixError};
1313
use std::{io, mem};
14-
use tar::{EntryType, Header};
14+
use tar::{Builder, EntryType, Header};
1515
use thiserror::Error;
1616
use tracing::{debug, trace};
1717
use uv_distribution_filename::{SourceDistExtension, SourceDistFilename, WheelFilename};
1818
use uv_fs::Simplified;
1919
use uv_globfilter::{parse_portable_glob, GlobDirFilter, PortableGlobError};
20-
use walkdir::WalkDir;
20+
use walkdir::{DirEntry, WalkDir};
2121
use zip::{CompressionMethod, ZipWriter};
2222

2323
#[derive(Debug, Error)]
@@ -66,6 +66,8 @@ pub enum Error {
6666
Csv(#[from] csv::Error),
6767
#[error("Expected a Python module with an `__init__.py` at: `{}`", _0.user_display())]
6868
MissingModule(PathBuf),
69+
#[error("Absolute module root is not allowed: `{}`", _0.display())]
70+
AbsoluteModuleRoot(PathBuf),
6971
#[error("Inconsistent metadata between prepare and build step: `{0}`")]
7072
InconsistentSteps(&'static str),
7173
#[error("Failed to write to {}", _0.user_display())]
@@ -292,11 +294,29 @@ fn write_hashed(
292294
})
293295
}
294296

297+
/// TODO(konsti): Wire this up with actual settings and remove this struct.
298+
///
299+
/// Which files to include in the wheel
300+
pub struct WheelSettings {
301+
/// The directory that contains the module directory, usually `src`, or an empty path when
302+
/// using the flat layout over the src layout.
303+
module_root: PathBuf,
304+
}
305+
306+
impl Default for WheelSettings {
307+
fn default() -> Self {
308+
Self {
309+
module_root: PathBuf::from("src"),
310+
}
311+
}
312+
}
313+
295314
/// Build a wheel from the source tree and place it in the output directory.
296315
pub fn build_wheel(
297316
source_tree: &Path,
298317
wheel_dir: &Path,
299318
metadata_directory: Option<&Path>,
319+
wheel_settings: WheelSettings,
300320
uv_version: &str,
301321
) -> Result<WheelFilename, Error> {
302322
let contents = fs_err::read_to_string(source_tree.join("pyproject.toml"))?;
@@ -319,7 +339,10 @@ pub fn build_wheel(
319339
let mut wheel_writer = ZipDirectoryWriter::new_wheel(File::create(&wheel_path)?);
320340

321341
debug!("Adding content files to {}", wheel_path.user_display());
322-
let strip_root = source_tree.join("src");
342+
if wheel_settings.module_root.is_absolute() {
343+
return Err(Error::AbsoluteModuleRoot(wheel_settings.module_root));
344+
}
345+
let strip_root = source_tree.join(wheel_settings.module_root);
323346
let module_root = strip_root.join(pyproject_toml.name().as_dist_info_name().as_ref());
324347
if !module_root.join("__init__.py").is_file() {
325348
return Err(Error::MissingModule(module_root));
@@ -337,6 +360,9 @@ pub fn build_wheel(
337360
let relative_path_str = relative_path
338361
.to_str()
339362
.ok_or_else(|| Error::NotUtf8Path(relative_path.to_path_buf()))?;
363+
364+
debug!("Adding to wheel: `{relative_path_str}`");
365+
340366
if entry.file_type().is_dir() {
341367
wheel_writer.write_directory(relative_path_str)?;
342368
} else if entry.file_type().is_file() {
@@ -514,44 +540,7 @@ pub fn build_source_dist(
514540
continue;
515541
};
516542

517-
debug!("Including {}", relative.user_display());
518-
519-
let metadata = fs_err::metadata(entry.path())?;
520-
let mut header = Header::new_gnu();
521-
#[cfg(unix)]
522-
{
523-
header.set_mode(std::os::unix::fs::MetadataExt::mode(&metadata));
524-
}
525-
#[cfg(not(unix))]
526-
{
527-
header.set_mode(0o644);
528-
}
529-
530-
if entry.file_type().is_dir() {
531-
header.set_entry_type(EntryType::Directory);
532-
header
533-
.set_path(Path::new(&top_level).join(relative))
534-
.map_err(|err| Error::TarWrite(source_dist_path.clone(), err))?;
535-
header.set_size(0);
536-
header.set_cksum();
537-
tar.append(&header, io::empty())
538-
.map_err(|err| Error::TarWrite(source_dist_path.clone(), err))?;
539-
continue;
540-
} else if entry.file_type().is_file() {
541-
header.set_size(metadata.len());
542-
header.set_cksum();
543-
tar.append_data(
544-
&mut header,
545-
Path::new(&top_level).join(relative),
546-
BufReader::new(File::open(entry.path())?),
547-
)
548-
.map_err(|err| Error::TarWrite(source_dist_path.clone(), err))?;
549-
} else {
550-
return Err(Error::UnsupportedFileType(
551-
relative.clone(),
552-
entry.file_type(),
553-
));
554-
}
543+
add_source_dist_entry(&mut tar, &entry, &top_level, &source_dist_path, &relative)?;
555544
}
556545

557546
tar.finish()
@@ -560,6 +549,55 @@ pub fn build_source_dist(
560549
Ok(filename)
561550
}
562551

552+
/// Add a file or a directory to a source distribution.
553+
fn add_source_dist_entry(
554+
tar: &mut Builder<GzEncoder<File>>,
555+
entry: &DirEntry,
556+
top_level: &str,
557+
source_dist_path: &Path,
558+
relative: &Path,
559+
) -> Result<(), Error> {
560+
debug!("Including {}", relative.user_display());
561+
562+
let metadata = fs_err::metadata(entry.path())?;
563+
let mut header = Header::new_gnu();
564+
#[cfg(unix)]
565+
{
566+
header.set_mode(std::os::unix::fs::MetadataExt::mode(&metadata));
567+
}
568+
#[cfg(not(unix))]
569+
{
570+
header.set_mode(0o644);
571+
}
572+
573+
if entry.file_type().is_dir() {
574+
header.set_entry_type(EntryType::Directory);
575+
header
576+
.set_path(Path::new(&top_level).join(relative))
577+
.map_err(|err| Error::TarWrite(source_dist_path.to_path_buf(), err))?;
578+
header.set_size(0);
579+
header.set_cksum();
580+
tar.append(&header, io::empty())
581+
.map_err(|err| Error::TarWrite(source_dist_path.to_path_buf(), err))?;
582+
Ok(())
583+
} else if entry.file_type().is_file() {
584+
header.set_size(metadata.len());
585+
header.set_cksum();
586+
tar.append_data(
587+
&mut header,
588+
Path::new(&top_level).join(relative),
589+
BufReader::new(File::open(entry.path())?),
590+
)
591+
.map_err(|err| Error::TarWrite(source_dist_path.to_path_buf(), err))?;
592+
Ok(())
593+
} else {
594+
Err(Error::UnsupportedFileType(
595+
relative.to_path_buf(),
596+
entry.file_type(),
597+
))
598+
}
599+
}
600+
563601
/// Write the dist-info directory to the output directory without building the wheel.
564602
pub fn metadata(
565603
source_tree: &Path,

crates/uv-build-backend/src/tests.rs

+16-2
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,14 @@ fn test_record() {
4646
fn test_determinism() {
4747
let temp1 = TempDir::new().unwrap();
4848
let uv_backend = Path::new("../../scripts/packages/uv_backend");
49-
build_wheel(uv_backend, temp1.path(), None, "1.0.0+test").unwrap();
49+
build_wheel(
50+
uv_backend,
51+
temp1.path(),
52+
None,
53+
WheelSettings::default(),
54+
"1.0.0+test",
55+
)
56+
.unwrap();
5057

5158
// Touch the file to check that we don't serialize the last modified date.
5259
fs_err::write(
@@ -56,7 +63,14 @@ fn test_determinism() {
5663
.unwrap();
5764

5865
let temp2 = TempDir::new().unwrap();
59-
build_wheel(uv_backend, temp2.path(), None, "1.0.0+test").unwrap();
66+
build_wheel(
67+
uv_backend,
68+
temp2.path(),
69+
None,
70+
WheelSettings::default(),
71+
"1.0.0+test",
72+
)
73+
.unwrap();
6074

6175
let wheel_filename = "uv_backend-0.1.0-py3-none-any.whl";
6276
assert_eq!(

crates/uv/src/commands/build_backend.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::commands::ExitStatus;
44
use anyhow::Result;
55
use std::env;
66
use std::path::Path;
7-
use uv_build_backend::SourceDistSettings;
7+
use uv_build_backend::{SourceDistSettings, WheelSettings};
88

99
pub(crate) fn build_sdist(sdist_directory: &Path) -> Result<ExitStatus> {
1010
let filename = uv_build_backend::build_source_dist(
@@ -24,6 +24,7 @@ pub(crate) fn build_wheel(
2424
&env::current_dir()?,
2525
wheel_directory,
2626
metadata_directory,
27+
WheelSettings::default(),
2728
uv_version::version(),
2829
)?;
2930
println!("{filename}");

0 commit comments

Comments
 (0)