@@ -11,13 +11,13 @@ use std::fs::FileType;
11
11
use std:: io:: { BufReader , Cursor , Read , Write } ;
12
12
use std:: path:: { Path , PathBuf , StripPrefixError } ;
13
13
use std:: { io, mem} ;
14
- use tar:: { EntryType , Header } ;
14
+ use tar:: { Builder , EntryType , Header } ;
15
15
use thiserror:: Error ;
16
16
use tracing:: { debug, trace} ;
17
17
use uv_distribution_filename:: { SourceDistExtension , SourceDistFilename , WheelFilename } ;
18
18
use uv_fs:: Simplified ;
19
19
use uv_globfilter:: { parse_portable_glob, GlobDirFilter , PortableGlobError } ;
20
- use walkdir:: WalkDir ;
20
+ use walkdir:: { DirEntry , WalkDir } ;
21
21
use zip:: { CompressionMethod , ZipWriter } ;
22
22
23
23
#[ derive( Debug , Error ) ]
@@ -66,6 +66,8 @@ pub enum Error {
66
66
Csv ( #[ from] csv:: Error ) ,
67
67
#[ error( "Expected a Python module with an `__init__.py` at: `{}`" , _0. user_display( ) ) ]
68
68
MissingModule ( PathBuf ) ,
69
+ #[ error( "Absolute module root is not allowed: `{}`" , _0. display( ) ) ]
70
+ AbsoluteModuleRoot ( PathBuf ) ,
69
71
#[ error( "Inconsistent metadata between prepare and build step: `{0}`" ) ]
70
72
InconsistentSteps ( & ' static str ) ,
71
73
#[ error( "Failed to write to {}" , _0. user_display( ) ) ]
@@ -292,11 +294,29 @@ fn write_hashed(
292
294
} )
293
295
}
294
296
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
+
295
314
/// Build a wheel from the source tree and place it in the output directory.
296
315
pub fn build_wheel (
297
316
source_tree : & Path ,
298
317
wheel_dir : & Path ,
299
318
metadata_directory : Option < & Path > ,
319
+ wheel_settings : WheelSettings ,
300
320
uv_version : & str ,
301
321
) -> Result < WheelFilename , Error > {
302
322
let contents = fs_err:: read_to_string ( source_tree. join ( "pyproject.toml" ) ) ?;
@@ -319,7 +339,10 @@ pub fn build_wheel(
319
339
let mut wheel_writer = ZipDirectoryWriter :: new_wheel ( File :: create ( & wheel_path) ?) ;
320
340
321
341
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 ) ;
323
346
let module_root = strip_root. join ( pyproject_toml. name ( ) . as_dist_info_name ( ) . as_ref ( ) ) ;
324
347
if !module_root. join ( "__init__.py" ) . is_file ( ) {
325
348
return Err ( Error :: MissingModule ( module_root) ) ;
@@ -337,6 +360,9 @@ pub fn build_wheel(
337
360
let relative_path_str = relative_path
338
361
. to_str ( )
339
362
. ok_or_else ( || Error :: NotUtf8Path ( relative_path. to_path_buf ( ) ) ) ?;
363
+
364
+ debug ! ( "Adding to wheel: `{relative_path_str}`" ) ;
365
+
340
366
if entry. file_type ( ) . is_dir ( ) {
341
367
wheel_writer. write_directory ( relative_path_str) ?;
342
368
} else if entry. file_type ( ) . is_file ( ) {
@@ -514,44 +540,7 @@ pub fn build_source_dist(
514
540
continue ;
515
541
} ;
516
542
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) ?;
555
544
}
556
545
557
546
tar. finish ( )
@@ -560,6 +549,55 @@ pub fn build_source_dist(
560
549
Ok ( filename)
561
550
}
562
551
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
+
563
601
/// Write the dist-info directory to the output directory without building the wheel.
564
602
pub fn metadata (
565
603
source_tree : & Path ,
0 commit comments